• Sign in
 
  •  
  • MSDN Blogs
  • Microsoft Blog Images
  • More ...

  • About
  • Email Blog Author
  • RSS for posts
  • Atom
  • RSS for comments
    • OK
  • CDO (25)
  • Code Snippet (43)
  • Custom Providers (17)
  • Debugging (7)
  • DevMsgTeam (301)
  • Documentation (109)
  • DST (8)
  • EWS (7)
  • Exchange (109)
  • Gotchas (97)
  • Hotfix (28)
  • MAPI (240)
  • MAPI Download (54)
  • MFCMAPI (101)
  • MSDN (59)
  • Non Dev (11)
  • OOM (17)
  • Outlook (171)
  • Outlook 2007 Auxiliary Reference (45)
  • Outlook Integration API (12)
  • Protocol Docs (20)
  • PST/OST (23)
  • Referrals (8)
  • Vista (12)
  • WrapPST (18)
Links:
  • Download MFCMAPI
  • MFCMAPI on Facebook
  • Troubleshooting Outlook Crashes
  • Office Update Center
  • Developer Messaging Team Blog
This site is provided "AS IS" with no warranties, and confers no rights. Use of included code samples are subject to the terms specified in the Terms of Use.
Archives
  • May 2013 (3)
  • April 2013 (1)
  • March 2013 (2)
  • February 2013 (2)
  • January 2013 (2)
  • December 2012 (4)
  • November 2012 (2)
  • October 2012 (2)
  • September 2012 (1)
  • August 2012 (3)
  • June 2012 (2)
  • May 2012 (1)
  • April 2012 (3)
  • March 2012 (3)
  • February 2012 (3)
  • January 2012 (1)
  • December 2011 (3)
  • November 2011 (1)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (4)
  • June 2011 (3)
  • May 2011 (3)
  • April 2011 (3)
  • March 2011 (5)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (1)
  • November 2010 (4)
  • October 2010 (1)
  • September 2010 (3)
  • August 2010 (5)
  • July 2010 (3)
  • June 2010 (3)
  • May 2010 (1)
  • April 2010 (3)
  • March 2010 (3)
  • February 2010 (3)
  • January 2010 (2)
  • December 2009 (3)
  • November 2009 (5)
  • October 2009 (4)
  • September 2009 (5)
  • August 2009 (5)
  • July 2009 (11)
  • June 2009 (6)
  • May 2009 (5)
  • April 2009 (3)
  • March 2009 (18)
  • February 2009 (10)
  • January 2009 (3)
  • December 2008 (2)
  • November 2008 (2)
  • October 2008 (5)
  • September 2008 (4)
  • August 2008 (10)
  • July 2008 (6)
  • June 2008 (8)
  • May 2008 (2)
  • April 2008 (4)
  • March 2008 (2)
  • February 2008 (2)
  • January 2008 (5)
  • December 2007 (3)
  • November 2007 (2)
  • October 2007 (3)
  • September 2007 (1)
  • August 2007 (4)
  • July 2007 (5)
  • June 2007 (3)
  • May 2007 (4)
  • April 2007 (1)
  • March 2007 (6)
  • February 2007 (3)
  • January 2007 (2)
  • December 2006 (4)
  • November 2006 (3)
  • October 2006 (1)
  • August 2006 (1)
  • June 2006 (5)
  • May 2006 (5)
  • December 2005 (1)
  • November 2005 (4)
  • October 2005 (2)
  • September 2005 (1)
  • April 2005 (3)
  • December 2004 (2)
  • September 2004 (2)
  • August 2004 (3)
  • July 2004 (3)
Blogs I Read
  • Exchange

  • Raymond Chen [MSFT]

  • Larry Osterman [MSFT]

  • Peter David

  • Aaron Margosis [MSFT]

  • Jason Johnston [MSFT]

  • Matt Stehle (MSFT)

  • Patrick Creehan [MSFT]

  • Ryan Gregg [MSFT]

    Outlook PM
  • WebDav 101

    Dan Bagley (MSFT)
  • Dave Vespa [MSFT]

  • Randy Topken

    Outlook EE

August, 2010

MSDN Blogs > SGriffin's MAPI Internals > August, 2010
  • Subscribe via RSS
Sort by: Most Recent | Most Views | Most Comments
Excerpt View | Full Post View
  • SGriffin's MAPI Internals

    Let’s Try To Hash This Out

    Posted over 3 years ago
    by Stephen Griffin - MSFT
    • 2 Comments

    [This is now documented here: http://msdn.microsoft.com/en-us/library/ff960307.aspx ]

    A few years ago, I blogged an algorithm to calculate the hash used by Windows Search in MAPI urls. This algorithm is now enshrined in the MSDN. I’ve been asked by development to blog some augmentations to this algorithm to bring it more in line with what we actually send to Search.

    1. The binary blob to be encoded will be the PR_ENTRYID of the store in most cases, but for cached Exchange stores (both public and private), the binary blob should be PR_MAPPING_SIGNATURE, found in the profile.
    2. After computing the hash for a public folder store’s binary blob, but before hashing in the OST path, we hash in the constant 0x2E505542 to make sure the hash is uniquely different from the private store’s hash. This constant represents the string “.PUB”.

    Below you’ll find updated code which incorporates these new bits, along with some support code which will pull the relevant bits from the profile which can be used to tell if a store is public or private, if it’s cached, and the path to the OST. If you were to incorporate this code into your own project, the function you’d call is ComputeStoreHash, which takes as its input the session pointer as well as PR_ENTRYID, PR_SERVICE_UID, and PR_MDB_PROVIDER from the message store table. The rest of the information it needs it gets from the profile. For output, this function returns the hash as computed from PR_MAPPING_SIGNATURE, if the store is a cached Exchange store, and the hash as computed from PR_ENTRYID.

    BTW – the HrEmsmdbUIDFromStore support function here is interesting. It’s a multi-ex aware replacement for using pbGlobalProfileSectionGuid to open the profile section for an Exchange mailbox. If you want, you could augment this function by having it look for PR_EMSMDB_LEGACY and reporting if the mailbox being checked is considered the Legacy mailbox which would match what you get with pbGlobalProfileSectionGuid.

    I’ve tested this code against my own stores and the hashes I get match with what I see in Windows Search. Look forward to a version of this code showing up in the next version of MFCMAPI in September. Enjoy!

    #define PR_PROFILE_OFFLINE_STORE_PATH_A PROP_TAG(PT_STRING8, 0x6610)
    
    #define PR_PROFILE_OFFLINE_STORE_PATH_W PROP_TAG(PT_UNICODE, 0x6610)
    
    #define CONFIG_OST_CACHE_PRIVATE  ((ULONG)0x00000180)
    
    #define CONFIG_OST_CACHE_PUBLIC   ((ULONG)0x00000400)
    
    
    
    HRESULT HrEmsmdbUIDFromStore(IMAPISession* pmsess,
    
                                 MAPIUID* puidService,
    
                                 MAPIUID* pEmsmdbUID)
    
    {
    
      if (!puidService) return MAPI_E_INVALID_PARAMETER;
    
      HRESULT hRes = S_OK;
    
    
    
      SRestriction mres = {0};
    
      SPropValue mval = {0};
    
      SRowSet* pRows = NULL;
    
      SRow* pRow = NULL;
    
      LPSERVICEADMIN spSvcAdmin = NULL;
    
      LPMAPITABLE spmtab = NULL;
    
    
    
      enum { eEntryID = 0, eSectionUid, eMax };
    
      static const SizedSPropTagArray(eMax, tagaCols) =
    
      {
    
        eMax,
    
        {
    
          PR_ENTRYID,
    
          PR_EMSMDB_SECTION_UID,
    
        }
    
      };
    
    
    
      hRes = pmsess->AdminServices(0, (LPSERVICEADMIN*)&spSvcAdmin);
    
      if (SUCCEEDED(hRes) && spSvcAdmin)
    
      {
    
        hRes = spSvcAdmin->GetMsgServiceTable(0, &spmtab);
    
        if (spmtab)
    
        {
    
          hRes = spmtab->SetColumns((LPSPropTagArray)&tagaCols, TBL_BATCH);
    
    
    
          mres.rt = RES_PROPERTY;
    
          mres.res.resProperty.relop = RELOP_EQ;
    
          mres.res.resProperty.ulPropTag = PR_SERVICE_UID;
    
          mres.res.resProperty.lpProp = &mval;
    
          mval.ulPropTag = PR_SERVICE_UID;
    
          mval.Value.bin.cb = sizeof(*puidService);
    
          mval.Value.bin.lpb = (LPBYTE)puidService;
    
    
    
          (void) spmtab->Restrict(&mres, 0);
    
          (void) spmtab->QueryRows(10, 0, &pRows);
    
    
    
          if (SUCCEEDED(hRes) && pRows && pRows->cRows)
    
          {
    
            pRow = &pRows->aRow[0];
    
    
    
            if (pEmsmdbUID && pRow)
    
            {
    
              if (PR_EMSMDB_SECTION_UID == pRow->lpProps[eSectionUid].ulPropTag &&
    
                pRow->lpProps[eSectionUid].Value.bin.cb == sizeof(*pEmsmdbUID))
    
              {
    
                memcpy(pEmsmdbUID, pRow->lpProps[eSectionUid].Value.bin.lpb, sizeof(*pEmsmdbUID));
    
              }
    
            }
    
          }
    
          FreeProws(pRows);
    
        }
    
        if (spmtab) spmtab->Release();
    
      }
    
      if (spSvcAdmin) spSvcAdmin->Release();
    
      return hRes;
    
    } // HrEmsmdbUIDFromStore
    
    
    
    bool FExchangePrivateStore(LPMAPIUID lpmapiuid)
    
    {
    
      if (!lpmapiuid) return false;
    
      return IsEqualMAPIUID(lpmapiuid, (LPMAPIUID)pbExchangeProviderPrimaryUserGuid);
    
    } // FExchangePrivateStore
    
    
    
    bool FExchangePublicStore(LPMAPIUID lpmapiuid)
    
    {
    
      if (!lpmapiuid) return false;
    
      return IsEqualMAPIUID(lpmapiuid, (LPMAPIUID)pbExchangeProviderPublicGuid);
    
    } // FExchangePublicStore
    
    
    
    DWORD ComputeHash(ULONG cbStoreEID, LPBYTE pbStoreEID, LPCSTR pszFileName, LPCWSTR pwzFileName, BOOL bPublicStore)
    
    {
    
      DWORD  dwHash = 0;
    
      ULONG  cdw    = 0;
    
      DWORD* pdw    = NULL;
    
      ULONG  cb     = 0;
    
      BYTE*  pb     = NULL;
    
      ULONG  i      = 0;
    
    
    
      if (!cbStoreEID || !pbStoreEID) return dwHash;
    
      // We shouldn't see both of these at the same time.
    
      if (pszFileName && pwzFileName) return dwHash;
    
    
    
      // Get the Store Entry ID
    
      // pbStoreEID is a pointer to the Entry ID
    
      // cbStoreEID is the size in bytes of the Entry ID
    
      pdw = (DWORD*)pbStoreEID;
    
      cdw = cbStoreEID / sizeof(DWORD);
    
    
    
      for (i = 0; i < cdw; i++)
    
      {
    
        dwHash = (dwHash << 5) + dwHash + *pdw++;
    
      }
    
    
    
      pb = (BYTE *)pdw;
    
      cb = cbStoreEID % sizeof(DWORD);
    
    
    
      for (i = 0; i < cb; i++)
    
      {
    
        dwHash = (dwHash << 5) + dwHash + *pb++;
    
      }
    
    
    
      if (bPublicStore)
    
      {
    
        // augment to make sure it is unique else could be same as the private store
    
        dwHash = (dwHash << 5) + dwHash + 0x2E505542; // this is '.PUB'
    
      }
    
    
    
      // You may want to also include the store file name in the hash calculation
    
      // pszFileName and pwzFileName are NULL terminated strings with the path and filename of the store
    
      if (pwzFileName)
    
      {
    
        while (*pwzFileName)
    
        {
    
          dwHash = (dwHash << 5) + dwHash + *pwzFileName++;
    
        }
    
      }
    
      else if (pszFileName)
    
      {
    
        while (*pszFileName)
    
        {
    
          dwHash = (dwHash << 5) + dwHash + *pszFileName++;
    
        }
    
      }
    
    
    
      // dwHash now contains the hash to be used. It should be written in hex when building a URL.
    
      return dwHash;
    
    } // ComputeHash
    
    
    
    void ComputeStoreHash(LPMAPISESSION lpMAPISession, LPSBinary lpEntryID, LPSBinary lpServiceUID, LPSBinary lpProviderUID, DWORD* lpdwSigHash, DWORD* lpdwEIDHash)
    
    {
    
      HRESULT hRes = S_OK;
    
      MAPIUID emsmdbUID = {0};
    
      LPPROFSECT lpProfSect = NULL;
    
      BOOL fPublicExchangeStore  = FExchangePublicStore((LPMAPIUID)lpProviderUID->lpb);
    
      BOOL fPrivateExchangeStore = FExchangePrivateStore((LPMAPIUID)lpProviderUID->lpb);
    
      BOOL fCached = false;
    
      LPSPropValue lpConfigProp = NULL;
    
      LPSPropValue lpPathPropA = NULL;
    
      LPSPropValue lpPathPropW = NULL;
    
      LPSPropValue lpMappingSig = NULL;
    
      LPSTR szPath = NULL; // do not free
    
      LPWSTR wzPath = NULL; // do not free
    
    
    
      // Get profile section
    
      if (lpServiceUID)
    
      {
    
        hRes = HrEmsmdbUIDFromStore(lpMAPISession,
    
          (LPMAPIUID) lpServiceUID->lpb,
    
          &emsmdbUID);
    
        if (SUCCEEDED(hRes))
    
        {
    
          hRes = lpMAPISession->OpenProfileSection(&emsmdbUID, NULL, 0, &lpProfSect);
    
        }
    
      }
    
      if (!lpServiceUID || FAILED(hRes))
    
      {
    
        // For Outlook 2003/2007, HrEmsmdbUIDFromStore may not succeed,
    
        // so use pbGlobalProfileSectionGuid instead
    
        hRes = lpMAPISession->OpenProfileSection((LPMAPIUID)pbGlobalProfileSectionGuid, NULL, 0, &lpProfSect);
    
      }
    
    
    
      if (lpProfSect)
    
      {
    
        hRes = HrGetOneProp(lpProfSect, PR_PROFILE_CONFIG_FLAGS, &lpConfigProp);
    
        if (SUCCEEDED(hRes) && PROP_TYPE(lpConfigProp->ulPropTag) != PT_ERROR)
    
        {
    
          if (fPrivateExchangeStore)
    
          {
    
            fCached = ((lpConfigProp->Value.l & CONFIG_OST_CACHE_PRIVATE) != 0);
    
          }
    
          if (fPublicExchangeStore)
    
          {
    
            fCached = ((lpConfigProp->Value.l & CONFIG_OST_CACHE_PUBLIC) == CONFIG_OST_CACHE_PUBLIC);
    
          }
    
        }
    
    
    
        if (fCached)
    
        {
    
          hRes = HrGetOneProp(lpProfSect, PR_PROFILE_OFFLINE_STORE_PATH_W, &lpPathPropW);
    
          if (FAILED(hRes))
    
          {
    
            hRes = HrGetOneProp(lpProfSect, PR_PROFILE_OFFLINE_STORE_PATH_A, &lpPathPropA);
    
          }
    
          if (SUCCEEDED(hRes))
    
          {
    
            if (lpPathPropW && lpPathPropW->Value.lpszW)
    
            {
    
              wzPath = lpPathPropW->Value.lpszW;
    
            }
    
            else if (lpPathPropA && lpPathPropA->Value.lpszA)
    
            {
    
              szPath = lpPathPropA->Value.lpszA;
    
            }
    
          }
    
          // If this is an Exchange store with an OST path, it's an OST, so we get the mapping signature
    
          if ((fPrivateExchangeStore || fPublicExchangeStore) && (wzPath || szPath))
    
          {
    
            hRes = HrGetOneProp(lpProfSect, PR_MAPPING_SIGNATURE, &lpMappingSig);
    
          }
    
        }
    
      }
    
      DWORD dwSigHash = NULL;
    
      if (lpMappingSig && PT_BINARY == PROP_TYPE(lpMappingSig->ulPropTag))
    
      {
    
        dwSigHash = ComputeHash(lpMappingSig->Value.bin.cb, lpMappingSig->Value.bin.lpb, NULL, NULL, fPublicExchangeStore);
    
      }
    
      DWORD dwEIDHash = ComputeHash(lpEntryID->cb, lpEntryID->lpb, szPath, wzPath, fPublicExchangeStore);
    
    
    
      if (lpdwSigHash) *lpdwSigHash = dwSigHash;
    
      if (lpdwEIDHash) *lpdwEIDHash = dwEIDHash;
    
    
    
      MAPIFreeBuffer(lpMappingSig);
    
      MAPIFreeBuffer(lpPathPropA);
    
      MAPIFreeBuffer(lpPathPropW);
    
      MAPIFreeBuffer(lpConfigProp);
    
      if (lpProfSect) lpProfSect->Release();
    
    } // ComputeStoreHash
    
    
    
  • SGriffin's MAPI Internals

    HrProcessConvActionForSentItem

    Posted over 3 years ago
    by Stephen Griffin - MSFT
    • 5 Comments

    [This is now documented here: http://msdn.microsoft.com/en-us/library/ff976788.aspx]

    If you’re like me, you spend your time using Depends to see what kinds of new and exciting functions are exported every time there’s a new release of Outlook. Ok – so I don’t really do that, but development did ask me to document one of those functions. Here we go:

    HrProcessConvActionForSentItem

    Performs post-send categorization on a mail item based on its PidTagConversationId.

    Exported by:

    outlook.exe
    Called by: Client
    Implemented by: Outlook
    HRESULT WINAPI HrProcessConvActionForSentItem(
        SBinary const *pmbinStoreEid,
        SBinary const *pmbinMsgEid,
        SBinary const *pmbinConvID,
        DWORD dwFlags)

    Parameters

    pmbinStoreEid

    [in] The PidTagEntryId of the store (or the PidTagStoreEntryId of the mail item).  Cannot be NULL or invalid.

    pmbinMsgEid

    [in] The PidTagEntryId of the mail item.  Cannot be NULL or invalid.

    pmbinConvID

    [in] The PidTagConversationId of the mail item.  Cannot be NULL or invalid.

    dwFlags

    [in] A bitmask of the following flags (recommend value is 0).  Unknown flags will result in E_INVALIDARG. The following flag can be set: 
    PCAFSIF_MSGEID_IS_SEARCH_KEY (0x00000001) – The pmbinMsgEid argument is actually the PidTagSearchKey of the message.  This is more expensive and should be avoided if a PidTagEntryId is available.

    Return Value

    S_OK on success, or a FAILED HRESULT on failure

    Remarks

    Categories are considered personal information and should not be transmitted outside the user’s mailbox.  This function should be called on the Sent Item copy (or equivalent) of the mail item only after the mail item has been sent.

    This function can only be called in process with Outlook.exe, such as from a COM Add-In. Attempts to call this function out of process will cause an access violation exception.

    [update: 8/23/2010] Got confirmation from dev that this function is in-proc only after my attempt to add it to MFCMAPI caused a crash.

  • SGriffin's MAPI Internals

    You Dirty Rat

    Posted over 3 years ago
    by Stephen Griffin - MSFT
    • 9 Comments

    [This is now documented here: http://msdn.microsoft.com/en-us/library/gg262878.aspx ]

    Another in the series of APIs Development asked me to document. This time, it’s a property you can request via IDispatch to ask if an item is dirty or not. Same as last time, I didn’t test this code, so I’d appreciate feedback.

    dispidFDirty

    This property is used to tell if an Outlook item (such as a MailItem or ContactItem) is in the Dirty state. An item is considered Dirty when it has unsaved changes.

    Syntax

    Given an item pointer, use QueryInterface to obtain an IDispatch interface pointer. Then call IDispatch::Invoke with dispidFDirty and the flags DISPATCH_METHOD | DISPATCH_PROPERTYGET.

    Sample Code

    #define dispidFDirty 0xF024
    
    
    
    bool FIsItemDirty(IDispatch *pdisp)
    
    {
    
        DISPPARAMS dispparams;
    
        UINT uArgErr;
    
        HRESULT hr = S_OK;
    
        CComVariant varDirty;
    
        dispparams.rgvarg = 0;
    
        dispparams.cArgs = 0;
    
        dispparams.cNamedArgs = 0;
    
        dispparams.rgdispidNamedArgs = NULL;
    
    
    
        hr = pdisp->Invoke(dispidFDirty,
    
            IID_NULL,
    
            LOCALE_SYSTEM_DEFAULT,
    
            DISPATCH_METHOD | DISPATCH_PROPERTYGET,
    
            &dispparams,
    
            &varDirty,
    
            NULL,
    
            &uArgErr);
    
        return SUCCEEDED(hr) && varDirty.bVal;
    
    }
  • SGriffin's MAPI Internals

    No Photos Please

    Posted over 3 years ago
    by Stephen Griffin - MSFT
    • 0 Comments

    [This is now documented here: http://msdn.microsoft.com/en-us/library/gg508913.aspx ]

    Development recently asked that I document a few oddball Outlook APIs. Here’s the first – a way to turn the Sender Contact Photo feature on or off in a particular inspector or explorer window. I’ve not tested the code snippet, so any feedback is welcome.

    dispidShowSenderPhoto

    This method is used to tell an Outlook inspector or explorer window that we do or do not wish to have Sender Contact Photos displayed in that particular window. This setting does not persist across Outlook sessions, and does not carry from one inspector or explorer to another. The default setting is True/On. On means that if a photo is present, it will be shown. However, a placeholder photo will not be shown if no photo is present.

    Syntax

    Given either an Explorer or Inspector object pointer, use QueryInterface to obtain an IDispatch interface pointer. Then call IDispatch::Invoke with dispidShowSenderPhoto and the desired value.

    This method takes one Boolean argument. Use VARIANT_TRUE to turn it on, and VARIANT_FALSE to turn it off.

    Registry Key Interaction

    This setting works in conjunction with the TurnOffPhotograph policy key (Outlook 2010 only) as well as the older ShowContactPicture registry key (Outlook 2007 and up). Here’s a grid which shows how these registry keys and dispidShowSenderPhoto interact:

    dispidShowSenderPhoto TurnOffPhotograph ShowContactPicture Will photos display if present?
    True 0 or not set 1 or not set Yes
    True 0 or not set 0 no
    True 1 1 or not set no
    True 1 0 no
    False 0 or not set 1 or not set no
    False 0 or not set 0 no
    False 1 1 or not set no
    False 1 0 no

    Sample Code

    #define dispidShowSenderPhoto 0xF0D0
    
    
    
    void SetSenderContactPhoto(_InspectorPtr inspector, bool showSenderContactPhoto)
    
    { 
    
        CComVariant cv;
    
        IDispatchPtr spdisp;
    
        DISPPARAMS dispparams;
    
        EXCEPINFO excepinfo = {0};
    
    
    
        spdisp = inspector;
    
    
    
        cv = showSenderContactPhoto ? VARIANT_TRUE : VARIANT_FALSE;
    
    
    
        dispparams.rgvarg = &cv;
    
        dispparams.cArgs = 1;
    
        dispparams.rgdispidNamedArgs = NULL;
    
        dispparams.cNamedArgs = 0;
    
    
    
        spdisp->Invoke(dispidShowSenderPhoto,
    
            IID_NULL,
    
            0,
    
            DISPATCH_METHOD,
    
            &dispparams,
    
            NULL,
    
            &excepinfo,
    
            NULL);
    
    }
    
    
    

    Enjoy!

  • SGriffin's MAPI Internals

    Throttling Exchange 2010 SP1

    Posted over 3 years ago
    by Stephen Griffin - MSFT
    • 0 Comments

    Remember when I talked about throttling in Exchange 2010? One of the drawbacks was that you could only configure throttling at the mailbox level, meaning if the account didn’t have a mailbox, you couldn’t control how it was throttled. This seriously affected (broke) cross forest and machine account scenarios. They were all governed by the fallback policy, which is hardcoded.

    Turns out, we’re fixing this in Exchange 2010 SP1. The Ehlo blog has a really good write-up of the problem and the way SP1 fixes it. If you’ve got a MAPI program that might be running as a machine or cross forest account, you’re gonna want to read this.

Page 1 of 1 (5 items)
  • © 2013 Microsoft Corporation.
  • Terms of Use
  • Trademarks
  • Privacy & Cookies
  • Report Abuse
  • 5.6.426.415