home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / trialva / ibmcppw / sdk / mapi / win16 / dev / sample.ms / mspmisc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  42.1 KB  |  1,491 lines

  1. /*
  2.  *  M S P M I S C . C
  3.  *
  4.  *  Utility functions needed by the MAPI Sample Store Provider.
  5.  *
  6.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  7.  */
  8.  
  9. #include "msp.h"
  10.  
  11. /*
  12.  *  Memory allocation and release functions.  They use the lpMalloc
  13.  *  interface supplied by MAPI during provider initialization.
  14.  *
  15.  *  For internal memory allocations the Sample Store Provider uses
  16.  *  the lpMalloc passed by MAPI on MSProviderInit().  For simplicity
  17.  *  of coding we keep the pointer to the lpMalloc in a
  18.  *  per-process global (which requires mucho work on Win16) instead
  19.  *  of forcing ScAlloc() calls to pass in the pointer on each and every
  20.  *  allocation.  We trade off code size with speed (on Win16) because
  21.  *  of the searching necessary in finding the per-instance globals.
  22.  *
  23.  *  When multiple MSProviderInit's are done on the same process
  24.  *  (by opening one or more stores on two different MAPI Profiles
  25.  *  on the same process) we use the lpMalloc from the first init
  26.  *  for all allocations, keeping a refcount of MSProviderInit's
  27.  *  that have been done in order to know when to free the lpMalloc.
  28.  *
  29.  *  As a result we don't necessary use the lpMalloc for store X that
  30.  *  was passed in when store X was opened, but as long as MAPI gives
  31.  *  us the Component Object's lpMalloc on each open we're fine.
  32.  */
  33.  
  34. SCODE ScAlloc(ULONG lcb, LPVOID * ppv)
  35. {
  36.     PINST pinst = (PINST) PvGetInstanceGlobals();
  37.  
  38.     Assert(pinst);
  39.     Assert(pinst->lpmalloc);
  40.  
  41.     *ppv = pinst->lpmalloc->lpVtbl->Alloc(pinst->lpmalloc, lcb);
  42.     if (*ppv != NULL)
  43.         return S_OK;
  44.  
  45.     DebugTraceSc(ScAlloc, MAPI_E_NOT_ENOUGH_MEMORY);
  46.     return (MAPI_E_NOT_ENOUGH_MEMORY);
  47. }
  48.  
  49. SCODE ScAllocZ(ULONG lcb, LPVOID * ppv)
  50. {
  51.     PINST pinst = (PINST) PvGetInstanceGlobals();
  52.  
  53.     Assert(pinst);
  54.     Assert(pinst->lpmalloc);
  55.  
  56.     *ppv = pinst->lpmalloc->lpVtbl->Alloc(pinst->lpmalloc, lcb);
  57.     if (*ppv != NULL)
  58.     {
  59.         memset(*ppv, 0, (size_t) lcb);
  60.         return S_OK;
  61.     }
  62.  
  63.     DebugTraceSc(ScAllocZ, MAPI_E_NOT_ENOUGH_MEMORY);
  64.     return (MAPI_E_NOT_ENOUGH_MEMORY);
  65. }
  66.  
  67. SCODE ScRealloc(ULONG lcb, LPVOID pvOrig, LPVOID * ppv)
  68. {
  69.     PINST pinst = (PINST) PvGetInstanceGlobals();
  70.     LPVOID pvNew;
  71.  
  72.     Assert(pinst);
  73.     Assert(pinst->lpmalloc);
  74.  
  75.     pvNew = pinst->lpmalloc->lpVtbl->Realloc(pinst->lpmalloc, pvOrig, lcb);
  76.     if (pvNew != NULL)
  77.     {
  78.         *ppv = pvNew;
  79.         return S_OK;
  80.     }
  81.  
  82.     DebugTraceSc(ScRealloc, MAPI_E_NOT_ENOUGH_MEMORY);
  83.     return (MAPI_E_NOT_ENOUGH_MEMORY);
  84. }
  85.  
  86. void FreeNull(LPVOID pv)
  87. {
  88.     if (pv)
  89.     {
  90.         PINST pinst = (PINST) PvGetInstanceGlobals();
  91.  
  92.         Assert(pinst);
  93.         Assert(pinst->lpmalloc);
  94.  
  95.         pinst->lpmalloc->lpVtbl->Free(pinst->lpmalloc, pv);
  96.     }
  97. }
  98.  
  99. /* Linked Memory Utilities ------------------------------------------------- */
  100.  
  101. SCODE LMAllocZ(PLMR plmr, ULONG lcb, LPVOID * ppv)
  102. {
  103.     SCODE sc;
  104.  
  105.     sc = LMAlloc(plmr, lcb, ppv);
  106.     if (sc != S_OK)
  107.         return (sc);
  108.  
  109.     memset(*ppv, 0, (size_t) lcb);
  110.     return (S_OK);
  111. }
  112.  
  113. /*
  114.  *  ScInitMSInstance
  115.  *
  116.  *  Remember the given lpMalloc in a per-instance variable that
  117.  *  can be looked up again in ScAlloc and FreeNull.
  118.  */
  119. SCODE ScInitMSInstance(LPMALLOC lpmalloc)
  120. {
  121.     PINST pinst = (PINST) PvGetInstanceGlobals();
  122.     SCODE sc;
  123.  
  124.     Assert(lpmalloc);
  125.  
  126.     if (pinst)
  127.     {
  128.         /* A usable allocator is already set.   */
  129.         /* Ignore the one we were passed in     */
  130.         /* and instead bump the refcount on the */
  131.         /* one we're going to use.              */
  132.         ++(pinst->cRef);
  133.         return S_OK;
  134.     }
  135.  
  136.     /* In our debugging version wrap the allocator for
  137.      * extra help locating memory leaks or other problems.
  138.      * In both debug and non-debug versions, this statement
  139.      * addrefs the allocator, and the DBGMEM_Shutdown at the
  140.      * end does a release on the allocator.
  141.      */
  142.     lpmalloc = DBGMEM_Encapsulate(lpmalloc, "Sample Store", 0);
  143.  
  144.     pinst = (PINST) lpmalloc->lpVtbl->Alloc(lpmalloc, sizeof(INST));
  145.  
  146.     if (pinst == NULL)
  147.     {
  148.         sc = MAPI_E_NOT_ENOUGH_MEMORY;
  149.         goto ret;
  150.     }
  151.  
  152.     sc = ScSetInstanceGlobals(pinst);
  153.     if (sc != S_OK)
  154.         goto ret;
  155.  
  156.     pinst->cRef = 1;
  157.     pinst->lpmalloc = lpmalloc;
  158.  
  159.     return S_OK;
  160.  
  161. ret:
  162.     if (pinst)
  163.         lpmalloc->lpVtbl->Free(lpmalloc, pinst);
  164.  
  165.     DBGMEM_Shutdown(lpmalloc);
  166.     DebugTraceSc(ScInitMSInstance, sc);
  167.     return sc;
  168. }
  169.  
  170. void DeinitMSInstance(void)
  171. {
  172.     PINST pinst = (PINST) PvGetInstanceGlobals();
  173.     LPMALLOC lpmalloc;
  174.  
  175.     if (!pinst || --(pinst->cRef) > 0)
  176.         return;
  177.  
  178.     (void)ScSetInstanceGlobals(NULL);
  179.     lpmalloc = pinst->lpmalloc;
  180.     lpmalloc->lpVtbl->Free(lpmalloc, pinst);
  181.     DBGMEM_Shutdown(lpmalloc);
  182. }
  183.  
  184. /*
  185.  *  SzBaseName
  186.  *
  187.  *  Purpose
  188.  *      return the base name of the object with the given eid
  189.  *
  190.  *  Parameters
  191.  *      peid                Pointer to entryID of object
  192.  *
  193.  *  Returns
  194.  *      LPTSTR              base name, ie sequence number and extension
  195.  *
  196.  *  Note                    no memory is allocated, returned value must be
  197.  *                          copied if it is to be saved.
  198.  */
  199. LPTSTR SzBaseName(PEID peid)
  200. {
  201.     LPTSTR szName;              /* local name of object in lpEntryID */
  202.     LPTSTR szLastSlash;         /* position of last slash in peid */
  203.  
  204.     Assert(peid);
  205.  
  206.     /* Note that the entryid pathname is always relative to the root of */
  207.     /* the store, and will therefore never contain a drive letter. */
  208.  
  209.     szLastSlash = SzFindLastCh(peid->szPath, '\\');
  210.  
  211.     if (NULL == szLastSlash)
  212.     {
  213.         szName = (LPTSTR) peid->szPath;
  214.     }
  215.     else
  216.     {
  217.         szName = szLastSlash + 1;
  218.     }
  219.  
  220.     return szName;
  221. }
  222.  
  223. /*
  224.  * FCheckEIDType
  225.  *
  226.  *  Purpose
  227.  *      This function checks the pathname extension portion of the given
  228.  *      entryid against the string given. Since files in the sample store
  229.  *      have extensions that depend on the type of object within, comparing
  230.  *      the extension will tell the caller whether the entryid is of the type
  231.  *      specified by the extension passed in.
  232.  *
  233.  *  Parameters
  234.  *      peid: A pointer to the entryid to check.
  235.  *      szExt: A pointer to the string containing the extension to compare.
  236.  *
  237.  *  Returns: TRUE if the extensions matched, FALSE otherwise.
  238.  */
  239. BOOL FCheckEIDType(PEID peid, LPSTR szExt)
  240. {
  241.     BYTE *pb;
  242.  
  243.     if (peid == NULL)
  244.         return FALSE;
  245.  
  246.     pb = (BYTE *) peid;
  247.     pb += CbEID(peid);
  248.     pb -= ((CCH_EXT + 1) * sizeof(TCHAR));
  249.  
  250.     return (lstrcmpi((LPTSTR) pb, szExt) == 0);
  251. }
  252.  
  253. /*
  254.  *  FIsRoot
  255.  *
  256.  *  Purpose     Determine if an EID is one for the root folder
  257.  *
  258.  *  Parameters  peid
  259.  *
  260.  *  Returns     TRUE or FALSE
  261.  */
  262. BOOL FIsRoot(PEID peid)
  263. {
  264.     return (NULL == peid) || *(peid->szPath) == '\0';
  265. }
  266.  
  267. /*
  268.  *  FIsFolder
  269.  *
  270.  *  Parameters  peid    pointer to entryid of object
  271.  *
  272.  *  Purpose     return TRUE if the entryID is one for a folder
  273.  *
  274.  *  Returns     TRUE If the entryID is one for a folder, FALSE otherwixe
  275.  *
  276.  */
  277. BOOL FIsFolder(PEID peid)
  278. {
  279.     /* root is a folder */
  280.     if (FIsRoot(peid))
  281.         return TRUE;
  282.  
  283.     return (FCheckEIDType(peid, FOLDER_EXT));
  284. }
  285.  
  286. /*
  287.  *  FIsUnsavedMsg
  288.  *
  289.  *  Purpose     Determine if a message has had changes saved
  290.  *
  291.  *  Parameters  pimsg: A pointer to the message object to check.
  292.  *
  293.  *  SideEffect
  294.  *              Will say that a message is saved if another open version
  295.  *              has saved it.
  296.  *
  297.  *  Returns     TRUE or FALSE
  298.  */
  299. BOOL FIsUnsavedMsg(PIMSG pimsg)
  300. {
  301.     return (FIsUnsavedEID((PEID) pimsg->peid));
  302. }
  303.  
  304. /*
  305.  *  HrDeconstructEID
  306.  *
  307.  *  Purpose:
  308.  *      Given an EID, return its component parts (MAPIUID, path to
  309.  *      file, and file name).  This is NOT a general path parser.
  310.  *      Instead, because we know the exact structure of the EIDs
  311.  *      and paths we construct, we can directly access the
  312.  *      individual components.
  313.  *
  314.  *  Parameters
  315.  *      peid        EID to deconstruct.
  316.  *      ppuid       Location in which to return a pointer to the
  317.  *                  EID's MAPIUID.
  318.  *      pszPath     Location in which to return a pointer to the
  319.  *                  EID's file path.
  320.  *      pszFile     Location in which to return a pointer to the
  321.  *                  EID's file name.
  322.  *
  323.  *  Returns:
  324.  *      HRESULT
  325.  *
  326.  *  Size effects:
  327.  *      None.
  328.  *
  329.  *  Errors:
  330.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate memory for
  331.  *                                  some or all of the return
  332.  *                                  parameters.
  333.  */
  334. HRESULT HrDeconstructEID(PEID peid, LPMAPIUID *ppuid, LPTSTR *pszPath,
  335.     LPTSTR *pszFile)
  336. {
  337.     SCODE sc;
  338.     LPMAPIUID puid = NULL;
  339.     LPTSTR szPath = NULL;
  340.     LPTSTR szFile = NULL;
  341.     LPTSTR szT = NULL;
  342.     LPTSTR szEnd = NULL;
  343.  
  344.     AssertSz(!IsBadReadPtr(peid, CbNewEID(0)), "Bad peid #1");
  345.     AssertSz(!IsBadReadPtr(peid, CbEID(peid)), "Bad peid #2");
  346.     AssertSz(!IsBadWritePtr(ppuid, sizeof(LPMAPIUID)), "Bad ppuid");
  347.     AssertSz(!IsBadWritePtr(pszPath, sizeof(LPTSTR)), "Bad pszPath");
  348.     AssertSz(!IsBadWritePtr(pszFile, sizeof(LPTSTR)), "Bad pszFile");
  349.  
  350.     *ppuid = NULL;
  351.     *pszPath = NULL;
  352.     *pszFile = NULL;
  353.  
  354.     /* Get the UID out */
  355.  
  356.     sc = ScAlloc(sizeof(MAPIUID), &puid);
  357.     if (sc != S_OK)
  358.         goto exit;
  359.     *puid = peid->uidResource;
  360.  
  361.     /* Get the path and file name out */
  362.     szT = SzFindLastCh(peid->szPath, '\\');
  363.     if (szT)
  364.         szT++;
  365.     else
  366.         szT = peid->szPath;
  367.  
  368.     szEnd = (TCHAR *) ((BYTE *) peid + CbEID(peid));
  369.     sc = ScAlloc((szEnd - szT) * sizeof(TCHAR), &szFile);
  370.     if (sc != S_OK)
  371.         goto exit;
  372.  
  373.     if ((LPBYTE) szEnd - (LPBYTE) szT)
  374.         memcpy(szFile, szT, (LPBYTE) szEnd - (LPBYTE) szT);
  375.  
  376.     if (szT != peid->szPath)
  377.     {
  378.         sc = ScAlloc((LPBYTE) szT - (LPBYTE) (peid->szPath), &szPath);
  379.         if (sc != S_OK)
  380.             goto exit;
  381.  
  382.         /* We copy the trailing backslash along with the rest of   */
  383.         /* the string, then overwrite it with the NULL terminator. */
  384.         memcpy(szPath, peid->szPath, (LPBYTE) szT - (LPBYTE) (peid->szPath));
  385.         *(szPath + (szT - peid->szPath) - 1) = '\0';
  386.     }
  387.     else
  388.     {
  389.         sc = ScAlloc(sizeof(TCHAR), (PPV) &szPath);
  390.         if (sc != S_OK)
  391.             goto exit;
  392.  
  393.         *szPath = '\0';
  394.     }
  395.  
  396. exit:
  397.     if (sc)
  398.     {
  399.         (void)FreeNull((LPVOID) puid);
  400.         (void)FreeNull((LPVOID) szPath);
  401.         (void)FreeNull((LPVOID) szFile);
  402.     }
  403.     else
  404.     {
  405.         *ppuid = puid;
  406.         *pszPath = szPath;
  407.         *pszFile = szFile;
  408.     }
  409.  
  410.     DebugTraceSc(HrDeconstructEID, sc);
  411.     return ResultFromScode(sc);
  412. }
  413.  
  414. /*
  415.  *  HrAppendPath
  416.  *
  417.  *  Purpose:
  418.  *      Concatenate a path onto another, allocating space for the
  419.  *      result.
  420.  *
  421.  *  Parameters
  422.  *      szBase          Beginning of concatenated path.
  423.  *      szAppend        End of concatenated path.
  424.  *      pszFullPath     Pointer in which to place a pointer to the
  425.  *                      newly allocated concatenated path.
  426.  *
  427.  *  Returns:
  428.  *      HRESULT
  429.  *
  430.  *  Side effects:
  431.  *      None.
  432.  *
  433.  *  Errors:
  434.  *      MAPI_E_NOT_ENOUGH_MEMORY    Unable to allocate memory for
  435.  *                                  the return variable.
  436.  */
  437. HRESULT HrAppendPath(LPTSTR szBase, LPTSTR szAppend, LPTSTR * pszFullPath)
  438. {
  439.     SCODE sc = S_OK;
  440.     TCHAR rgch[512];
  441.  
  442.     AssertSz(szBase, "Bad szBase");
  443.     AssertSz(szAppend, "Bad szAppend");
  444.     AssertSz(pszFullPath, "Bad pszFullPath");
  445.     AssertSz(szAppend[0] != '\\',
  446.         "szAppend not relative path");
  447.  
  448.     if (!FAppendPathNoMem(szBase, szAppend, sizeof rgch / sizeof rgch[0],
  449.             rgch))
  450.     {
  451.         sc = MAPI_E_STRING_TOO_LONG;
  452.         goto exit;
  453.     }
  454.  
  455.     sc = ScAlloc(Cbtszsize(rgch), (PPV) pszFullPath);
  456.     if (sc != S_OK)
  457.         goto exit;
  458.  
  459.     lstrcpy(*pszFullPath, rgch);
  460.  
  461. exit:
  462.     DebugTraceSc(HrAppendPath, sc);
  463.     return ResultFromScode(sc);
  464. }
  465.  
  466. /*
  467.  *  FAppendPathNoMem
  468.  *
  469.  *  Purpose:
  470.  *      Concatenate two parts of a file path, returning result in a
  471.  *      preallocated buffer.
  472.  *
  473.  *  Parameters
  474.  *      szBase          First part of path to concatenate.
  475.  *      szAppend        Second part of path to concatenate.
  476.  *      cchFullPath     Size of return buffer.
  477.  *      szFullPath      Return buffer.
  478.  *
  479.  *  Returns:
  480.  *      BOOL.  TRUE if the return buffer is large enough to hold
  481.  *      the resultant string, FALSE if the buffer is not large
  482.  *      enough (value in szFullPath is undefined).
  483.  *
  484.  *  Side effects:
  485.  *      None.
  486.  *
  487.  *  Errors:
  488.  *      None.
  489.  */
  490. BOOL FAppendPathNoMem(LPTSTR szBase, LPTSTR szAppend, ULONG cchFullPath,
  491.     LPTSTR szFullPath)
  492. {
  493.     UINT cchBase = 0;
  494.     UINT cchAppend = 0;
  495.     UINT cchFull = 0;
  496.     BOOLEAN fPostSlash = FALSE;
  497.  
  498.     AssertSz(szBase, "Bad szBase");
  499.     AssertSz(szAppend, "Bad szAppend");
  500.     AssertSz(szFullPath, "Bad szFullPath");
  501.     AssertSz(szAppend[0] != '\\',
  502.         "szAppend not relative path");
  503.  
  504.     cchBase = lstrlen(szBase);
  505.     cchAppend = lstrlen(szAppend);
  506.  
  507.     /* Check if szBase has trailing backslash, else we'll need to add it */
  508.  
  509.     if (*(szBase + cchBase - 1) == '\\')
  510.     {
  511.         fPostSlash = TRUE;
  512.         cchFull = cchBase + cchAppend + 1;
  513.     }
  514.     else
  515.     {
  516.         fPostSlash = FALSE;
  517.         cchFull = cchBase + cchAppend + 2;
  518.     }
  519.  
  520.     if (cchFull <= cchFullPath)
  521.     {
  522.         lstrcpy(szFullPath, szBase);
  523.         if (!fPostSlash)
  524.         {
  525.             lstrcat(szFullPath, TEXT("\\"));
  526.         }
  527.         lstrcat(szFullPath, szAppend);
  528.  
  529.         return TRUE;
  530.     }
  531.  
  532.     return FALSE;
  533. }
  534.  
  535. /*
  536.  *  ReplaceExt
  537.  *
  538.  *  Purpose:
  539.  *      Substitute the filename extension in szFile with the new
  540.  *      one passed in as szExt.  It is the caller's
  541.  *      responsibility to ensure that the filename has room for the
  542.  *      new extension.  Also, the extension must include the
  543.  *      period.
  544.  *
  545.  *  Parameters
  546.  *      szFile      Filename on which to operate.
  547.  *      szExt       New extension.
  548.  *
  549.  *  Returns:
  550.  *      void.
  551.  */
  552. void ReplaceExt(LPTSTR szFile, LPTSTR szExt)
  553. {
  554.     LPTSTR szT = NULL;
  555.  
  556.     AssertSz(!IsBadStringPtr(szFile, (UINT) -1), "Bad szFile");
  557.     AssertSz(!IsBadStringPtr(szExt, (UINT) -1), "Bad szExt");
  558.  
  559.     szT = SzFindLastCh(szFile, '.');
  560.  
  561.     if (szT)
  562.         lstrcpy(szT, szExt);
  563.     #ifdef DEBUG
  564.     else
  565.         TrapSz("No extension found on szFile. Not replacing extension.");
  566.     #endif
  567.  
  568.     return;
  569. }
  570.  
  571. /*
  572.  *  HrConstructEID
  573.  *
  574.  *  Purpose     return an new EntryID for an object
  575.  *
  576.  *  Parameters
  577.  *      puidStore       UID for the store containing the object
  578.  *      plmr            Pointer to the MAPI linked memory allocators.
  579.  *      szNewName       new root relative path name of the object
  580.  *      ppeidNew        address of pointer to new entryID
  581.  *
  582.  *  Returns:
  583.  *      ULONG, PEID
  584.  */
  585. HRESULT HrConstructEID(LPMAPIUID puidStore, PLMR plmr, LPSTR szNewName,
  586.     PEID *ppeidNew)
  587. {
  588.     PEID peidNew;               /* new entry id */
  589.     ULONG cbEID;                /* number of bytes in the new entry id */
  590.     ULONG cbNewName;
  591.     SCODE sc;
  592.  
  593.     cbNewName = lstrlen(szNewName) + 1; /* we count the NULL terminator */
  594.     cbEID = CbNewEID(cbNewName);
  595.  
  596.     /* allocate space for the new entry id */
  597.     /* Use the MAPI allocator because it may be returned to the client */
  598.     /* Note that we zero-fill this allocation so that the entryid produced */
  599.     /* by identical input parameters will always be the same. If we didn't */
  600.     /* zero fill, the pad bytes after the version would be randomly filled. */
  601.  
  602.     sc = LMAllocZ(plmr, cbEID, (PPV) &peidNew);
  603.     if (sc != S_OK)
  604.         goto exit;
  605.  
  606.     *(DWORD *) (peidNew->abFlags) = (DWORD) 0;
  607.     peidNew->uidResource = *puidStore;
  608.     peidNew->bVersion = SMPMS_VERSION;
  609.  
  610.     lstrcpy(peidNew->szPath, szNewName);
  611.  
  612.     if (peidNew->szPath[0])
  613.         AnsiLowerBuff(peidNew->szPath, lstrlen(peidNew->szPath));
  614.  
  615.     *ppeidNew = peidNew;
  616.  
  617. exit:
  618.     DebugTraceSc(HrConstructEID, sc);
  619.     return ResultFromScode(sc);
  620. }
  621.  
  622. /*
  623.  * HrGetParentEID
  624.  *
  625.  *  Purpose         construct an entry id for the parent of peid. This
  626.  *                  assumes that all files in the sample message store 
  627.  *                  are CCH_NAME chars long (including NULL).
  628.  *
  629.  *  Parameters
  630.  *      plmr        Pointer to the MAPI linked memory allocators.
  631.  *      peid        entry id of object whose parent is requested
  632.  *      ppeidParent pointer to parent's peid
  633.  *
  634.  */
  635. HRESULT HrGetParentEID(PLMR plmr, PEID peid, PEID *ppeidParent)
  636. {
  637.     HRESULT hr;
  638.     ULONG cbParentName = sizeof(TCHAR); /* number of bytes in szParentName */
  639.     LPTSTR szParentName = NULL; /* name of parent */
  640.  
  641.     if (CbEIDPath(peid) > (CCH_NAME * sizeof(TCHAR)))
  642.         cbParentName = CbEIDPath(peid) - (CCH_NAME * sizeof(TCHAR));
  643.  
  644.     hr = HrAlloc(cbParentName, (PPV) &szParentName);
  645.     if (hr != hrSuccess)
  646.         goto exit;
  647.  
  648.     if (cbParentName > sizeof(TCHAR))
  649.         memcpy(szParentName, peid->szPath, (UINT) cbParentName);
  650.     szParentName[cbParentName - sizeof(TCHAR)] = 0;
  651.  
  652.     hr = HrConstructEID(&(peid->uidResource), plmr, szParentName,
  653.         ppeidParent);
  654.  
  655. exit:
  656.     FreeNull(szParentName);
  657.     DebugTraceResult(HrGetParentEID, hr);
  658.     return hr;
  659. }
  660.  
  661. /*
  662.  * HrOpenParent
  663.  *
  664.  * Purpose  open the parent folder of the given entry id
  665.  *
  666.  * Parameters
  667.  *  pims    store in which the object is
  668.  *  peid    entry id of object whose parent is to be opened
  669.  *  ulFlags MAPI_MODIFY if you want write permission on the store
  670.  *  ppifld  pointer to variable to hold open parent
  671.  */
  672. HRESULT HrOpenParent(PIMS pims, PEID peid, ULONG ulFlags, PIFLD * ppifld)
  673. {
  674.     HRESULT hr = hrSuccess;
  675.     PEID peidParent = NULL;
  676.     ULONG ulObjectType;
  677.  
  678.     Assert(pims);
  679.     Assert(peid);
  680.     Assert(ppifld);
  681.  
  682.     *ppifld = NULL;
  683.     hr = HrGetParentEID(&pims->lmr, peid, &peidParent);
  684.     if (hr != hrSuccess)
  685.         goto exit;
  686.  
  687.     hr = pims->lpVtbl->OpenEntry(pims, CbEID(peidParent),
  688.         (LPENTRYID) peidParent, NULL, ulFlags, &ulObjectType,
  689.         (LPUNKNOWN *) ppifld);
  690.     if (hr != hrSuccess)
  691.         goto exit;
  692.  
  693.     Assert(ulObjectType == MAPI_FOLDER);
  694.  
  695. exit:
  696.     LMFree(&pims->lmr, peidParent);
  697.  
  698.     DebugTraceResult(HrOpenParent, hr);
  699.     return hr;
  700. }
  701.  
  702. /*
  703.  * FreePropArrays
  704.  *
  705.  * Purpose      deallocate space for PropTag, PropValue and PropAttr arrays
  706.  *              allocated with HrAllocPropArrays
  707.  * Parameters
  708.  *  ppval       address of property value array
  709.  *  pptaga  address of the property tag array
  710.  *  ppatra  address of the property attribute array
  711.  */
  712. void FreePropArrays(LPSPropValue *ppval, LPSPropTagArray *pptaga,
  713.     LPSPropAttrArray *ppatra)
  714. {
  715.     Assert(ppval);
  716.     Assert(pptaga);
  717.     Assert(ppatra);
  718.  
  719.     FreeNull(*ppval);
  720.     FreeNull(*pptaga);
  721.     FreeNull(*ppatra);
  722.  
  723.     *ppval = NULL;
  724.     *pptaga = NULL;
  725.     *ppatra = NULL;
  726. }
  727.  
  728. /*
  729.  * HrAllocPropArrays
  730.  *
  731.  * Purpose      allocate space for PropTag, PropValue and PropAttr arrays
  732.  *              Free with FreeNull or FreePropArrays
  733.  * Parameters
  734.  *  cProps  number of properties in the arrays
  735.  *  ppval   address of property value array
  736.  *  pptaga  address of the property tag array
  737.  *  ppatra  address of the property attribute array
  738.  */
  739. HRESULT HrAllocPropArrays(ULONG cProps, LPSPropValue *ppval,
  740.     LPSPropTagArray *pptaga, LPSPropAttrArray *ppatra)
  741. {
  742.     HRESULT hr;
  743.  
  744.     /* All three pointers must be provided. */
  745.     Assert(!IsBadWritePtr(ppval, sizeof(LPSPropValue)));
  746.     Assert(!IsBadWritePtr(pptaga, sizeof(LPSPropTagArray)));
  747.     Assert(!IsBadWritePtr(ppatra, sizeof(LPSPropAttrArray)));
  748.  
  749.     /* All must be zero on entry for our cleanup mechanism. */
  750.     AssertSz(!*ppval, "bad ppval");
  751.     AssertSz(!*pptaga, "bad pptaga");
  752.     AssertSz(!*ppatra, "bad ppatra");
  753.  
  754.     hr = HrAlloc(cProps * sizeof(SPropValue), ppval);
  755.     if (hr != hrSuccess)
  756.         goto exit;
  757.  
  758.     hr = HrAlloc(CbNewSPropTagArray(cProps), pptaga);
  759.     if (hr != hrSuccess)
  760.         goto exit;
  761.  
  762.     hr = HrAlloc(CbNewSPropAttrArray(cProps), ppatra);
  763.     if (hr != hrSuccess)
  764.         goto exit;
  765.  
  766. exit:
  767.     if (hr != hrSuccess)
  768.         FreePropArrays(ppval, pptaga, ppatra);
  769.  
  770.     DebugTraceResult(HrAllocPropArrays, hr);
  771.     return hr;
  772. }
  773.  
  774. /*
  775.  * ProcessGetProps
  776.  *
  777.  *  Purpose
  778.  *      Helper routine from HrWrap_GetProps. Folder and message objects in
  779.  *      the sample store keep a few property values in memory so that when
  780.  *      we move or copy a folder or message, the entryid, parent entryid,
  781.  *      and (for copied messages) record key are correct. This routine 
  782.  *      overrides the value returned from IMessage for any in-memory 
  783.  *      properties. We keep a very small placeholder property for each
  784.  *      of the in-memory properties on disk and mark it read-only and 
  785.  *      not deletable. This keeps SetProps from succeeding on these
  786.  *      properties. All properties that we override in this function are
  787.  *      PT_BINARY property types, so HrWrap_GetProps only calls this 
  788.  *      function if the property it is looking for is a PT_BINARY.
  789.  *
  790.  *      This function tries to find the property that the client is 
  791.  *      requesting in the in-memory array of properties stored with the
  792.  *      object. If the routine finds the property, then it tries to allocate
  793.  *      and copy the correct data into the client's array. If the allocation
  794.  *      fails, the routine fills in a PT_ERROR into the client's property
  795.  *      value. Note that HrWrap_GetProps needs to watch for PT_ERROR coming
  796.  *      back and return the appropriate warning to the client. 
  797.  *      If the routine doesn't find the property, then it returns to
  798.  *      HrWrap_GetProps without modifying the client's array at all.
  799.  *
  800.  *  Parameters
  801.  *      pvalClient: A pointer to the property value to search for in our
  802.  *          in-memory property array. This function may MODIFY this value.
  803.  *      cvalInt: The number of in-memory properties on this object. If the
  804.  *          object has no in-memory properties, this value will be zero.
  805.  *      pvalInt: A pointer to the object's array of in-memory properties.
  806.  *      plmr: A pointer to the linked memory allocation routines.
  807.  *      pvOrig: The original allocated pointer (AllocateMore needs this).
  808.  *
  809.  *  Returns
  810.  *      None. May have modified the client's property value if there was
  811.  *      a matching entry in the in-memory array.
  812.  */
  813. static void ProcessGetProps(LPSPropValue pvalClient, ULONG cvalInt,
  814.     LPSPropValue pvalInt, PLMR plmr, LPVOID pvOrig)
  815. {
  816.     ULONG ulClientID = PROP_ID(pvalClient->ulPropTag);
  817.     ULONG ulClientType = PROP_TYPE(pvalClient->ulPropTag);
  818.     LPSPropValue pvalT;
  819.     LPSPropValue pvalTMac;
  820.  
  821.     pvalT = pvalInt;
  822.     pvalTMac = pvalT + cvalInt;
  823.  
  824.     for (; pvalT < pvalTMac; ++pvalT)
  825.     {
  826.         LPVOID pv;
  827.         ULONG cb;
  828.         SCODE sc;
  829.  
  830.         AssertSz(PROP_TYPE(pvalT->ulPropTag) == PT_BINARY,
  831.             "Code assumes all internal props are PT_BINARY");
  832.  
  833.         if (ulClientID == PROP_ID(pvalT->ulPropTag))
  834.         {
  835.             cb = pvalT->Value.bin.cb;
  836.             pv = pvalT->Value.bin.lpb;
  837.  
  838.             /* Link onto returned data our extra info. */
  839.             sc = LMAllocMore(plmr, cb, pvOrig, &pvalClient->Value.bin.lpb);
  840.             if (sc != S_OK)
  841.             {
  842.                 pvalClient->Value.err = sc;
  843.                 pvalClient->ulPropTag = PROP_TAG(PT_ERROR, ulClientID);
  844.             }
  845.             else
  846.             {
  847.                 pvalClient->Value.bin.cb = cb;
  848.                 if (cb)
  849.                     memcpy(pvalClient->Value.bin.lpb, pv, (UINT) cb);
  850.                 pvalClient->ulPropTag = PROP_TAG(PT_BINARY, ulClientID);
  851.             }
  852.  
  853.             break;
  854.         }
  855.     }
  856.  
  857.     return;
  858. }
  859.  
  860. /*
  861.  * HrWrap_GetProps
  862.  *
  863.  * Purpose
  864.  *  Adjust return from GetProps (Store, Folder, Message, etc) for wrapped
  865.  *  values of PR_STORE_ENTRYID, PR_STORE_RECORD_KEY and (if a store)
  866.  *  PR_ENTRYID. Doesn't allow the return of PR_ENTRYID for an unsaved
  867.  *  message. Also overwrites values for properties that the object has
  868.  *  cached in memory using the helper routine ProcessGetProps.
  869.  *              
  870.  * Parameters
  871.  *  hr              HRESULT from the original GetProps call.
  872.  *  pims            pointer to the message store object
  873.  *  cvalInt         the number of property values that are held in memory 
  874.  *                  associated with the object.
  875.  *  pvalInt         a pointer to the array of in-memory properties associated
  876.  *                  with the object. May be NULL if cvalInt is 0.
  877.  *  pcValues        The number of values in client's PropValue array
  878.  *  ppval           a pointer to the client's PropValue array
  879.  *  fStore          TRUE if the object given the GetProps call was the
  880.  *                  message store object.
  881.  *  fTagsSpecified  TRUE if the client specified a proptag array on the call;
  882.  *                  FALSE if the client passed NULL for the proptag array.
  883.  *
  884.  * Coding comments:
  885.  *  The objects must contain these properties, of the appropriate
  886.  *  type, but it isn't necessary that their values IN the objects be
  887.  *  accurate. The KEYS, for example, don't even have to be 16 bytes.
  888.  *
  889.  *  The result code may be adjusted depending on whether this routine
  890.  *  or the underlying property implementation, ran out of memory
  891.  *  while dealing with one of these.
  892.  *
  893.  *  The PR_RECORD_KEY (a UID) in a Store IS the right value,
  894.  *  and does not need to be wrapped.
  895.  */
  896. HRESULT HrWrap_GetProps(HRESULT hr, PIMS pims, ULONG cvalInt,
  897.     LPSPropValue pvalInt, ULONG * pcValues, LPSPropValue * ppval,
  898.     BOOL fStore, BOOL fTagsSpecified)
  899. {
  900.     /* Warning: pcValues and ppval parameters may not           */
  901.     /* have been validated.  Do not dereference them unless     */
  902.     /* the "hr" says everything succeeded so far.               */
  903.  
  904.     BOOL fErrors = FALSE;
  905.     LPSPropValue pval;
  906.     LPSPropValue pvalMac;
  907.  
  908.     /* No work to do unless the GetProps() generally succeeded. */
  909.     if (HR_FAILED(hr))
  910.         goto exit;
  911.  
  912.     pval = *ppval;
  913.     pvalMac = pval + *pcValues;
  914.  
  915.     for (; pval < pvalMac; ++pval)
  916.     {
  917.         UINT ulType = (UINT) PROP_TYPE(pval->ulPropTag);
  918.         UINT ulID;
  919.         LPVOID pv;
  920.         ULONG cb;
  921.         SCODE sc;
  922.  
  923.         /* Update PR_STORE_SUPPORT_MASK appropriately based on how the */
  924.         /* client opened the store. */
  925.  
  926.         if (    fStore
  927.             &&  pval->ulPropTag == PR_STORE_SUPPORT_MASK)
  928.         {
  929.             if (OBJ_TestFlag(pims, OBJF_MODIFY))
  930.             {
  931.                 pval->Value.ul |= 
  932.                       STORE_MODIFY_OK
  933.                     | STORE_CREATE_OK
  934.                     | STORE_SUBMIT_OK;
  935.             }
  936.             else
  937.                 pval->Value.ul |= STORE_READONLY;
  938.         }
  939.  
  940.         if (ulType != PT_BINARY && ulType != PT_ERROR)
  941.             continue;
  942.  
  943.         if (ulType == PT_ERROR && pval->Value.err == MAPI_E_UNEXPECTED_TYPE)
  944.         {
  945.             fErrors = TRUE;
  946.             continue;
  947.         }
  948.  
  949.         if (cvalInt != 0)
  950.         {
  951.             ProcessGetProps(pval, cvalInt, pvalInt, &pims->lmr,
  952.                 (LPVOID) *ppval);
  953.  
  954.             /* Recompute the prop type in case ProcessGetProps changed it. */
  955.             ulType = (UINT) PROP_TYPE(pval->ulPropTag);
  956.         }
  957.  
  958.         /* These values should be computed here just in case ProcessGetProps */
  959.         /* modifies pval. */
  960.  
  961.         ulID = (UINT) PROP_ID(pval->ulPropTag);
  962.  
  963.         if (ulID == PROP_ID(PR_STORE_RECORD_KEY))
  964.         {
  965.             cb = sizeof(pims->uidResource);
  966.             pv = &pims->uidResource;
  967.         }
  968.         else if (ulID == PROP_ID(PR_STORE_ENTRYID)
  969.             || (fStore && ulID == PROP_ID(PR_ENTRYID)))
  970.         {
  971.             cb = pims->eidStore.cb;
  972.             pv = pims->eidStore.lpb;
  973.         }
  974.         else if (ulID == PROP_ID(PR_ENTRYID) && ulType != PT_ERROR)
  975.         {
  976.             /* entryid of a message doesn't exist until SaveChanges(). */
  977.             if (FIsUnsavedEID((PEID) pval->Value.bin.lpb))
  978.             {
  979.                 if (fTagsSpecified)
  980.                 {
  981.                     fErrors = TRUE;
  982.                     pval->ulPropTag = PROP_TAG(PT_ERROR, ulID);
  983.                     pval->Value.err = MAPI_E_NOT_FOUND;
  984.                 }
  985.                 else
  986.                 {
  987.                     /* The client wants all properties, and did not */
  988.                     /* specify a prop tag array. The client therefore */
  989.                     /* doesn't want NOT_FOUND errors. */
  990.                     /* Overwrite the error entry with the last SPropValue */
  991.                     /* in the array. */
  992.  
  993.                     (*pcValues)--;
  994.                     pvalMac--;
  995.  
  996.                     if (pval < pvalMac)
  997.                     {
  998.                         memcpy(pval, pvalMac, sizeof(SPropValue));
  999.                         --pval; /* redo this value, since we just changed it */
  1000.                     }
  1001.                 }
  1002.             }
  1003.             continue;
  1004.         }
  1005.         else
  1006.         {
  1007.             /* Remember if any errors occur in final array. */
  1008.             if (ulType == PT_ERROR)
  1009.             {
  1010.                 if (    pval->Value.err == MAPI_E_NOT_FOUND
  1011.                     &&  !fTagsSpecified)
  1012.                 {
  1013.                     /* The client wants all properties, and did not */
  1014.                     /* specify a prop tag array. The client therefore */
  1015.                     /* doesn't want NOT_FOUND errors. */
  1016.                     /* Overwrite the error entry with the last SPropValue */
  1017.                     /* in the array. */
  1018.  
  1019.                     (*pcValues)--;
  1020.                     pvalMac--;
  1021.  
  1022.                     if (pval < pvalMac)
  1023.                     {
  1024.                         memcpy(pval, pvalMac, sizeof(SPropValue));
  1025.                         --pval; /* redo this value, since we just changed it */
  1026.                     }
  1027.                 }
  1028.                 else
  1029.                     fErrors = TRUE;
  1030.             }
  1031.             continue;
  1032.         }
  1033.  
  1034.         /* Link onto returned data our extra info. */
  1035.         sc = LMAllocMore(&pims->lmr, cb, (LPVOID) *ppval,
  1036.             &pval->Value.bin.lpb);
  1037.         if (sc != S_OK)
  1038.         {
  1039.             fErrors = TRUE;
  1040.             pval->Value.err = sc;
  1041.             pval->ulPropTag = PROP_TAG(PT_ERROR, ulID);
  1042.         }
  1043.         else
  1044.         {
  1045.             pval->Value.bin.cb = cb;
  1046.             if (cb)
  1047.                 memcpy(pval->Value.bin.lpb, pv, (UINT) cb);
  1048.             pval->ulPropTag = PROP_TAG(PT_BINARY, ulID);
  1049.         }
  1050.     }
  1051.  
  1052.     /* Adjust HRESULT based on PT_ERRORs now. */
  1053.     if (!fErrors)
  1054.         hr = hrSuccess;
  1055.     else if (hr == hrSuccess)
  1056.         hr = ResultFromScode(MAPI_W_ERRORS_RETURNED);
  1057.  
  1058. exit:
  1059.     #ifdef DEBUG
  1060.     if (GetScode(hr) != MAPI_W_ERRORS_RETURNED)
  1061.         DebugTraceResult(HrWrap_GetProps, hr);
  1062.     #endif
  1063.  
  1064.     return hr;
  1065. }
  1066.  
  1067. /*
  1068.  * FIsSubmittedMessage
  1069.  *
  1070.  * Purpose      return TRUE if the message (specified by entryid) is submitted.
  1071.  *
  1072.  * Parameters
  1073.  *  pims        A pointer to the message store object.
  1074.  *  peid        The entryid of message to check.
  1075.  */
  1076. BOOL FIsSubmittedMessage(PIMS pims, PEID peid)
  1077. {
  1078.     HRESULT hr;
  1079.     PIMSG pimsgT = NULL;
  1080.     ULONG ulObjType;
  1081.  
  1082.     hr = pims->lpVtbl->OpenEntry(pims, CbEID(peid), (LPENTRYID) peid,
  1083.         NULL, MAPI_MODIFY, &ulObjType, (LPUNKNOWN *) &pimsgT);
  1084.  
  1085.     UlRelease(pimsgT);
  1086.  
  1087.     return (GetScode(hr) == MAPI_E_SUBMITTED);
  1088. }
  1089.  
  1090. /*
  1091.  *  HrOpenIMsgSession
  1092.  *
  1093.  *  Purpose:
  1094.  *      Open an IMsgSession, and return the pointer to the caller.
  1095.  *
  1096.  *  Parameters
  1097.  *      ppmsgsess: Pointer to the location to return the opened msg session.
  1098.  *
  1099.  *  Returns:
  1100.  *      HRESULT
  1101.  */
  1102. HRESULT HrOpenIMsgSession(LPMSGSESS *ppmsgsess)
  1103. {
  1104.     PINST pinst = (PINST) PvGetInstanceGlobals();
  1105.  
  1106.     Assert(pinst);
  1107.     Assert(pinst->lpmalloc);
  1108.  
  1109.     if (pinst == NULL)
  1110.         return ResultFromScode(MAPI_E_CALL_FAILED);
  1111.  
  1112.     return ResultFromScode(OpenIMsgSession(pinst->lpmalloc, 0L, ppmsgsess));
  1113. }
  1114.  
  1115. /*
  1116.  *  HrOpenIMsg
  1117.  *
  1118.  *  Purpose:
  1119.  *      Open the file given as a docfile, and then create an IMSG.DLL
  1120.  *      object on top of the storage.
  1121.  *
  1122.  *  Parameters
  1123.  *      pmsgsess    The message session to open the message within.
  1124.  *                  May be NULL (for ConfirmCred in msplogon to work).
  1125.  *      szFile      The file to open.
  1126.  *      plmr        a pointer to the linked memory routines.
  1127.  *      psup        a pointer to the MAPI support object.
  1128.  *      fCreate     TRUE means the caller wants to create the storage.
  1129.  *                  FALSE means open an existing storage.
  1130.  *      fModify     TRUE means the caller wants read/write access.
  1131.  *                  FALSE means read-only access. (This argument is 
  1132.  *                  ignored when fCreate is TRUE; in that case, the
  1133.  *                  file is always opened read/write.)
  1134.  *      fExclusive  TRUE means the caller wants exclusive access to the
  1135.  *                  storage, and to fail creation if the storage already
  1136.  *                  exists.
  1137.  *      ppmsg       Address of a location in which to return a
  1138.  *                  pointer to the newly opened IMessage instance.
  1139.  *
  1140.  *  Returns:
  1141.  *      HRESULT
  1142.  *
  1143.  *  Side effects:
  1144.  *      None.
  1145.  *
  1146.  *  Errors:
  1147.  *      IMessage on IStorage opening errors.
  1148.  */
  1149. HRESULT HrOpenIMsg(LPMSGSESS pmsgsess, LPSTR szFile, PLMR plmr, LPMAPISUP psup,
  1150.     BOOL fCreate, BOOL fModify, BOOL fExclusive, LPMESSAGE *ppmsg)
  1151. {
  1152.     HRESULT hr;
  1153.     SCODE sc;
  1154.     DWORD grfMode;
  1155.     LPSTORAGE lpstg = NULL;
  1156.     PINST pinst = (PINST) PvGetInstanceGlobals();
  1157.  
  1158. #ifdef WIN32
  1159.     OLE_CHAR szOle[MAX_PATH];
  1160.     int cbFile = 0L;
  1161. #else
  1162.     OLE_CHAR *szOle = NULL;
  1163. #endif
  1164.  
  1165.     Assert(pinst);
  1166.     Assert(pinst->lpmalloc);
  1167.  
  1168. #ifdef WIN32
  1169.     cbFile = 1 + lstrlen(szFile);
  1170.     Assert(cbFile < MAX_PATH);
  1171.  
  1172.     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szFile, cbFile, szOle, cbFile);
  1173. #else
  1174.     szOle = szFile;
  1175. #endif
  1176.  
  1177.     grfMode = STGM_TRANSACTED;
  1178.  
  1179.     if (fExclusive)
  1180.     {
  1181.         grfMode |= STGM_SHARE_EXCLUSIVE;
  1182.  
  1183.         if (fCreate)
  1184.             grfMode |= STGM_FAILIFTHERE;
  1185.     }
  1186.     else
  1187.         grfMode |= STGM_SHARE_DENY_NONE;
  1188.  
  1189.     if (fCreate)
  1190.     {
  1191.         grfMode |= (STGM_READWRITE | STGM_CREATE);
  1192.  
  1193.         hr = StgCreateDocfile(szOle, grfMode, 0, &lpstg);
  1194.  
  1195.         /* Commit docfile changes.  If we don't do this now, the file on  */
  1196.         /* disk will NOT be a docfile (i.e. OLE2 will not recognize it as */
  1197.         /* a docfile) if opened again with no other changes made to it.   */
  1198.  
  1199.         if (hr == hrSuccess)
  1200.             hr = lpstg->lpVtbl->Commit(lpstg, 0);
  1201.     }
  1202.     else
  1203.     {
  1204.         if (fModify)
  1205.             grfMode |= STGM_READWRITE;
  1206.         else
  1207.             grfMode |= STGM_READ;
  1208.  
  1209.         hr = StgOpenStorage(szOle, NULL, grfMode, NULL, 0, &lpstg);
  1210.     }
  1211.  
  1212.     if (hr != hrSuccess)
  1213.     {
  1214.         sc = MapStorageSCode(GetScode(hr));
  1215.         goto exit;
  1216.     }
  1217.  
  1218.     sc = OpenIMsgOnIStg(pmsgsess, plmr->lpAllocBuf, plmr->lpAllocMore,
  1219.         plmr->lpFreeBuf, pinst->lpmalloc, psup, lpstg, NULL, 0, 0, ppmsg);
  1220.  
  1221.     UlRelease(lpstg);
  1222.  
  1223. exit:
  1224.     if (sc != S_OK && fCreate)
  1225.         DeleteFile(szFile);
  1226.  
  1227.     DebugTraceSc(HrOpenIMsg, sc);
  1228.     return ResultFromScode(sc);
  1229. }
  1230.  
  1231. /*
  1232.  * HrSetOneROProp
  1233.  *
  1234.  *  Purpose
  1235.  *      The sample store needs to change properties that the client isn't
  1236.  *      allowed to change. This function allows the sample store to change
  1237.  *      a single property in the underlying IMessage object by first
  1238.  *      setting the attributes on that property to allow it to be written,
  1239.  *      then writing the property, and finally, setting the attributes back
  1240.  *      to once again only allow reading. Note that if the sample store 
  1241.  *      calls this routine on a property that is writable, this routine
  1242.  *      will make it non-writable.
  1243.  *
  1244.  *  Parameters
  1245.  *      lpmsg: A pointer to the IMessage object in which to set the property.
  1246.  *      plmr: A pointer to the linked memory allocation routines.
  1247.  *      ulPT: The property tag to set within the object.
  1248.  *      pv: A pointer to the property value to set.
  1249.  */
  1250. HRESULT HrSetOneROProp(LPMESSAGE lpmsg, PLMR plmr, ULONG ulPT, LPVOID pv)
  1251. {
  1252.     HRESULT hr;
  1253.     LPSPropAttrArray patra = NULL;
  1254.     LPSPropProblemArray pprba = NULL;
  1255.  
  1256.     SizedSPropTagArray(1, spta);
  1257.  
  1258.     /* Should be changing the pval array inside the object. */
  1259.  
  1260.     AssertSz(   ulPT != PR_ENTRYID
  1261.             &&  ulPT != PR_PARENT_ENTRYID
  1262.             &&  ulPT != PR_RECORD_KEY
  1263.             &&  ulPT != PR_INSTANCE_KEY, "Changing internal props in IMSG");
  1264.  
  1265.     spta.cValues = 1;
  1266.     spta.aulPropTag[0] = ulPT;
  1267.  
  1268.     /* Get current attributes and make the properties writable */
  1269.  
  1270.     hr = GetAttribIMsgOnIStg(lpmsg, (LPSPropTagArray) &spta, &patra);
  1271.     if (hr != hrSuccess)
  1272.         goto exit;
  1273.  
  1274.     patra->aPropAttr[0] |= PROPATTR_WRITEABLE;
  1275.  
  1276.     hr = SetAttribIMsgOnIStg(lpmsg, (LPSPropTagArray) &spta, patra, &pprba);
  1277.     if (hr != hrSuccess || pprba)
  1278.         goto exit;
  1279.  
  1280.     hr = HrSetSingleProp((LPMAPIPROP) lpmsg, plmr, ulPT, pv);
  1281.     if (hr != hrSuccess)
  1282.         goto exit;
  1283.  
  1284.     /* Restore the attribute */
  1285.  
  1286.     patra->aPropAttr[0] &= ~PROPATTR_WRITEABLE;
  1287.  
  1288.     hr = SetAttribIMsgOnIStg(lpmsg, (LPSPropTagArray) &spta, patra, &pprba);
  1289.     if (hr != hrSuccess || pprba)
  1290.         goto exit;
  1291.  
  1292. exit:
  1293.     if (pprba)
  1294.     {
  1295.         LMFree(plmr, pprba);
  1296.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1297.     }
  1298.  
  1299.     LMFree(plmr, patra);
  1300.  
  1301.     DebugTraceResult(HrSetOneROProp, hr);
  1302.     return hr;
  1303. }
  1304.  
  1305. /*
  1306.  * HrGetSingleProp
  1307.  *
  1308.  *  Purpose
  1309.  *      Gets a property from an object, and returns the value by stuffing
  1310.  *      it into a separately passed pointer. This function is nice because
  1311.  *      it doesn't require the caller to have an SPropValue around simply
  1312.  *      to retrieve the value of a property that is a known size.
  1313.  *
  1314.  *  Parameters
  1315.  *      pmprop  The property object to get the property from.
  1316.  *      plmr    Pointer to MAPI's linked memory routines.
  1317.  *      ulPT    The property tag to get.
  1318.  *      pv      A pointer to the location to place the value of the property.
  1319.  *
  1320.  *  Returns
  1321.  *      HRESULT. No warnings are returned because this function retrieves
  1322.  *      only one property at a time.
  1323.  */
  1324. HRESULT HrGetSingleProp(LPMAPIPROP pmprop, PLMR plmr, ULONG ulPT, LPVOID pv)
  1325. {
  1326.     LPSPropValue pval = NULL;
  1327.     SCODE sc;
  1328.     HRESULT hr;
  1329.     ULONG cValues;
  1330.  
  1331.     SizedSPropTagArray(1, spta);
  1332.  
  1333.     spta.cValues = 1;
  1334.     spta.aulPropTag[0] = ulPT;
  1335.  
  1336.     hr = pmprop->lpVtbl->GetProps(pmprop, (LPSPropTagArray) &spta, 0, /* ansi */
  1337.         &cValues, &pval);
  1338.  
  1339.     sc = GetScode(hr);
  1340.  
  1341.     if ((sc != S_OK)
  1342.         && (sc != MAPI_W_ERRORS_RETURNED))
  1343.         goto exit;
  1344.  
  1345.     switch (PROP_TYPE(pval->ulPropTag))
  1346.     {
  1347.     case PT_I2:
  1348.     case PT_BOOLEAN:
  1349.         Assert(!IsBadWritePtr(pv, sizeof(short)));
  1350.         *(short *)pv = pval->Value.i;
  1351.         break;
  1352.  
  1353.     case PT_LONG:
  1354.     case PT_R4:
  1355.         Assert(!IsBadWritePtr(pv, sizeof(LONG)));
  1356.         *(LONG *) pv = pval->Value.l;
  1357.         break;
  1358.  
  1359.     case PT_DOUBLE:
  1360.     case PT_APPTIME:
  1361.     case PT_SYSTIME:
  1362.     case PT_I8:
  1363.     case PT_CURRENCY:
  1364.         Assert(!IsBadWritePtr(pv, sizeof(LARGE_INTEGER)));
  1365.         *(LARGE_INTEGER *) pv = pval->Value.li;
  1366.         break;
  1367.  
  1368.     case PT_ERROR:
  1369.         sc = pval->Value.err;
  1370.         break;
  1371.  
  1372.     default:
  1373.         TrapSz1("Unimplemented PROP_TYPE %08lX passed in.",
  1374.             PROP_TYPE(pval->ulPropTag));
  1375.     }
  1376.  
  1377.     LMFree(plmr, pval);
  1378.  
  1379. exit:
  1380.     AssertSz1(sc <= 0, "Logic error: sc %s returned from HrGetSingleProp.",
  1381.         SzDecodeScode(sc));
  1382.  
  1383.     DebugTraceSc(HrGetSingleProp, sc);
  1384.     return (ResultFromScode(sc));
  1385. }
  1386.  
  1387. /*
  1388.  *  HrSetSingleProp
  1389.  *
  1390.  *  Purpose:
  1391.  *      Sets one property and its separately passed value. This function
  1392.  *      is nice because it doesn't require the caller to have a SPropValue
  1393.  *      to pass in.
  1394.  *
  1395.  *  Parameters
  1396.  *      pmprop  The property object to set the property into.
  1397.  *      plmr    Pointer to MAPI's linked memory routines.
  1398.  *      ulPT    The property to set
  1399.  *      pv      A pointer to the value of the property
  1400.  *
  1401.  *  Returns:
  1402.  *  Any errors from SetProps. Note that no warnings or problem arrays are
  1403.  *      returned because this routine only sets one property.
  1404.  */
  1405. HRESULT HrSetSingleProp(LPMAPIPROP pmprop, PLMR plmr, ULONG ulPT, LPVOID pv)
  1406. {
  1407.     HRESULT hr;
  1408.     SPropValue sval;
  1409.     LPSPropProblemArray pprba = NULL;
  1410.  
  1411.     sval.ulPropTag = ulPT;
  1412.  
  1413.     switch (PROP_TYPE(ulPT))
  1414.     {
  1415.     case PT_I2:
  1416.     case PT_BOOLEAN:
  1417.         sval.Value.i = *(short *)pv;
  1418.         break;
  1419.  
  1420.     case PT_LONG:
  1421.     case PT_R4:
  1422.     case PT_UNICODE:
  1423.     case PT_STRING8:
  1424.     case PT_CLSID:
  1425.         AssertSz(sizeof(LPVOID) == sizeof(LONG),
  1426.             "Pointers are not the size of a long on this machine");
  1427.         sval.Value.l = *(LONG *) pv;
  1428.         break;
  1429.  
  1430.     case PT_DOUBLE:
  1431.     case PT_APPTIME:
  1432.     case PT_SYSTIME:
  1433.     case PT_I8:
  1434.     case PT_CURRENCY:
  1435.     case PT_OBJECT:
  1436.     case PT_BINARY:
  1437.         sval.Value.li = *(LARGE_INTEGER *) pv;
  1438.         break;
  1439.  
  1440.     default:
  1441.         TrapSz1("Unimplemented PROP_TYPE %08lX passed in.", PROP_TYPE(ulPT));
  1442.     }
  1443.  
  1444.     hr = pmprop->lpVtbl->SetProps(pmprop, 1, &sval, &pprba);
  1445.  
  1446.     if (hr == hrSuccess && pprba)
  1447.         hr = ResultFromScode(pprba->aProblem[0].scode);
  1448.  
  1449.     LMFree(plmr, pprba);
  1450.  
  1451.     DebugTraceResult(HrSetSingleProp, hr);
  1452.     return hr;
  1453. }
  1454.  
  1455. /*
  1456.  *  FContainsProp
  1457.  *
  1458.  *  Purpose:
  1459.  *      returns whether or not a PropTag exists in a PropTagArray
  1460.  *
  1461.  *  Parameters
  1462.  *      ulPropTag   The property to search for.
  1463.  *      ptaga       A pointer to the SPropTagArray to search. May be null,
  1464.  *                  in which case, the function will return FALSE.
  1465.  *
  1466.  *  Returns:
  1467.  *      TRUE    if ulPropTag is in ptaga
  1468.  *      FALSE   if not
  1469.  */
  1470. BOOL FContainsProp(ULONG ulPropTag, LPSPropTagArray ptaga)
  1471. {
  1472.     ULONG *pulPropTag;
  1473.     ULONG *pulPropMax;
  1474.  
  1475.     if (!ptaga)
  1476.         return FALSE;
  1477.  
  1478.     pulPropTag = ptaga->aulPropTag;
  1479.     pulPropMax = pulPropTag + ptaga->cValues;
  1480.  
  1481.     while (pulPropTag < pulPropMax)
  1482.     {
  1483.         if (ulPropTag == *pulPropTag)
  1484.             return TRUE;
  1485.  
  1486.         pulPropTag++;
  1487.     }
  1488.  
  1489.     return FALSE;
  1490. }
  1491.