none
SystemAccessViolation getting properties of many recipients, some without a valid email addr RRS feed

  • Question

  • I am working on an Outlook 2010 AddIn.  When the user selects an email, we retrieve basic properties for each recipient (like: email address, first name, last name, etc.)  Normally, this works fine with no problems.

    However, sometimes some of the recipients on the email are not valid.  If you do a Reply All to the email, you can see Outlook telling you this. 

    If the number of recipients on the email is small, the invalid recipients do not cause a problem.  I am not certain what the threshold is, but 20 seems to work fine.

    However, if the number of recipients gets large (in the particular email I was working on, there were 315 recipients), then at some point you will get an AccessViolationException when accessing the properties of the ExchangeUser which is associated with the Recipient.  The exception happens whether you use normal VSTO properties (like ExchangeUser.PrimarySmtpAddress) or if you use the GetProperty method on the AddressEntry for the recipient.  Curiously, it doesn't seem to happen immediately at the time when you get the properties of the invalid recipient.  It happens one or two recipients later.  And again, if you create a draft for a ReplyAll and remove all the invalid recipients from it, when you open up that with your add-in, all is good.

    [Edit: Removed work-around which does not actually work]

    Jean Libera





    • Edited by Jean Libera Wednesday, February 22, 2012 8:35 PM
    Tuesday, February 21, 2012 11:47 PM

Answers

  • For now, I think we are going to go with the work-around I described above, which is to check the PR_SMTP_ADDRESS property for the Recipient, and if it returns MAPI_E_NOT_FOUND, then we do not get any properties from the AddressEntry or ExchangeUser objects. 

    Jean Libera

    Friday, February 24, 2012 5:46 PM

All replies

  • Hi Jean,

    Thanks for posting in the MSDN Forum.

    It's based on my experience that your instance properties must have a Null value as an AccessViolationException occurred. I think whether your go iterate all of the recipients, and move all of the recipients which have a Null value of the PrimarySmtpAddress. At the end please call the method ResolveAll of the recipient. I hope it can work for you.

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, February 22, 2012 2:19 AM
    Moderator
  • Your workaround sure sounds like you open too many objects at the same time.

    What is your code that loops through the recipients?


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.2 is now available!

    Wednesday, February 22, 2012 6:42 AM
  • public class EmailData : IEquatable<EmailData>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string CompanyName { get; set; }
        public string Email { get; set; }
        public string OutlookName { get; set; }
     
        /// <summary>
        /// If this property is true, it means that an outlook item
        /// has an OutlookExchange address which is no longer hooked
        /// up to an smtp email address.  This often happens when
        /// someone has left the company.
        /// </summary>
        public bool IsOutOfServiceExchangeAddress { get; set; }
           
        // other code (including Equals) omitted
    }


    public class ComWrapper
    {
        public static void ReleaseComObject(object comObject)
        {
            try
            {
                if (comObject != null)
                {
                    if (Marshal.IsComObject(comObject))
                    {
                        int refCount = Marshal.ReleaseComObject(comObject);
                        comObject = null;
                    }
                }
            }
            catch (System.Exception ex)
            {
                    // code to log the exception omitted
            }
        }
    }


    public List<EmailData> GetAllEmailModels(MailItem mailItem)
    {
        List<EmailData> emailList = new List<EmailData>();

        Recipients recipients = null;
        try
        {
            recipients = mailItem.Recipients;
        }
        catch (System.Exception ex)
        {
            // code to log the exception omitted          
           return emailList;
        }

        // purposely use for instead of foreach to avoid COM object leaks
        int count = recipients.Count;
        for (int i = 1; i <= count; ++i)
        {
            AddressEntry addressEntry = null;
            Recipient recipient = null;
                   
            try
            {
                recipient = recipients[i];
                if (recipient.Type == 0)
                {
                    ComWrapper.ReleaseComObject(recipient);
                    continue;
                }
                
                addressEntry = recipient.AddressEntry;
            }
            catch (System.Exception ex)
            {
                // code to log the exception omitted
                 
                ComWrapper.ReleaseComObject(recipient);
                ComWrapper.ReleaseComObject(addressEntry);
                continue;
            }


            EmailData emailData = GetEmailData(addressEntry);
            if (!emailList.Contains(emailData))
                emailList.Add(emailData);
                   
            ComWrapper.ReleaseComObject(recipient);
            ComWrapper.ReleaseComObject(addressEntry);
        }
       
        ComWrapper.ReleaseComObject(recipients);
       
        return emailList;
    }
               

    public EmailData GetEmailData(AddressEntry addressEntry)
    {
        if (addressEntry == null || addressEntry.Address == null)
        {
            return new EmailData();
        }

        EmailData emailData = new EmailData();

        ExchangeUser exchangeUser = null;
        ExchangeDistributionList distributionList = null;
        ContactItem contactItem = null;

        try
        {
            emailData.OutlookName = addressEntry.Name;

            if (addressEntry.Type == "EX")
            {
                exchangeUser = addressEntry.GetExchangeUser();

                if (exchangeUser != null)
                {
                    if (!String.IsNullOrEmpty(exchangeUser.FirstName))
                    {
                        emailData.FirstName = exchangeUser.FirstName;
                    }

                    emailData.LastName = exchangeUser.LastName;

                    emailData.CompanyName = exchangeUser.CompanyName;

                    emailData.Email = exchangeUser.PrimarySmtpAddress;

                    if (string.IsNullOrEmpty(emailData.Email))
                    {
                         emailData.IsOutOfServiceExchangeAddress = true;
                    }
                }

                else
                {
                     // code to handle distribution lists and contacts omitted
                }

                return emailData;

            } // end if type is "EX"
        }
        catch (System.Exception ex)
        {
            // code to log the exception omitted
        }
        finally
        {
            ComWrapper.ReleaseComObject(exchangeUser);
            ComWrapper.ReleaseComObject(distributionList);
            ComWrapper.ReleaseComObject(contactItem);                   
        }

        // code to get information if there is no exchange server account omitted
    }

    Wednesday, February 22, 2012 2:44 PM
  • Looks perfectly fine to me.

    Do you still see the error if you comment out the GetEmailData() implementation?


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.2 is now available!

    Wednesday, February 22, 2012 2:48 PM
  • If you comment out GetEmailData(), all is fine.  It is the retrieval of properties from the ExchangeUser object which is causing the problems, and that all happens in GetEmailData(). 

    I believe that this problem is solved by identifying the invalid recipients before even getting the ExchangeUser object.  Then if a recipient is invalid, avoid getting the ExchangeUser object.  Here are the 2 ways that we have found to do that:

    1. If you get the Smtp address from the Recipient, this will work for most recipients, but it won't work for the invalid ones.  Note that you can't use the SmtpAddress property on ExchangeUser, because that is diving into ExchangeUser.  Instead, you have to use PropertyAccessor.GetProperty() on the Recipient obect.  The property to use is
    string PR_SMTP_ADDRESS = @http://schemas.microsoft.com/mapi/proptag/0x39FE001E
    For the invalid recipients, this address will not be found.  (Outlook Spy shows MAPI_E_NOT_FOUND)

    2. If you get the AddressEntry.Address, a normal Exchange ("EX") address looks like this:
    "/o=Reed_Elsevier/ou=LEXIS-NEXIS-Oakbrook/cn=Recipients/cn=jlibera".
    But for the invalid recipients, we instead see an address that starts like this:
    "/O=NT5/ou=00000000000000000000000000000000"
    I am not certain what this means.  Should we be searching for this exact string, or only for part of it?  Perhaps one of the experts on this forum can provide some guildance


    • Edited by Jean Libera Wednesday, February 22, 2012 8:53 PM typo
    Wednesday, February 22, 2012 8:52 PM
  • I have a vague recollection that IAddrBook::OpenEntry would sometimes produce an access violation in Outlook 2003 (AFAIK fixed in an SP).

    What is your version of Outlook?


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.2 is now available!

    Friday, February 24, 2012 6:27 AM
  • I am using Outlook2010 SP1.  From the Help window and About box:

    Version: 14.0.6106.5005 (32-bit)

    Microsoft Outlook 2010 (14.0.6025.1000) SP1 MSO (14.0.6106.5005)

    Friday, February 24, 2012 3:07 PM
  • I can only sugggest opening a support case with Microsoft or switching to another API - either Extended MAPI (C++ or Delphi only) or Redemption (any language).


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.2 is now available!

    Friday, February 24, 2012 4:59 PM
  • For now, I think we are going to go with the work-around I described above, which is to check the PR_SMTP_ADDRESS property for the Recipient, and if it returns MAPI_E_NOT_FOUND, then we do not get any properties from the AddressEntry or ExchangeUser objects. 

    Jean Libera

    Friday, February 24, 2012 5:46 PM