none
Exception in Outlook 2010 add in RRS feed

  • Question

  • I am facing a exception with a addin that we have written for outlook 2010. Basically i am calculating the size of the emails.

    Following are the details of the exception

    Message: Not implemented (Exception from HRESULT: 0x80004001 (E_NOTIMPL)) -Not implemented (Exception from HRESULT: 0x80004001 (E_NOTIMPL)) - at Microsoft.Office.Interop.Outlook._MailItem.get_Size() at MFToolHelper.getFolderItemsSize(Items fItems) Category: Exception Priority: -1 EventId: 0 Severity: Error Title:LogErrorMessage : at Microsoft.Office.Interop.Outlook._MailItem.get_Size() Win32 ThreadId:5960


    muzammil ahmed

    Wednesday, April 11, 2012 10:07 PM

Answers

  • That's the problem with using Outlook objects in secondary threads: they might appear to work normal, but can and do fail at the worst possible moment.

    You can use something like the following to calculate the message size:

    set Folder = Application.ActiveExplorer.CurrentFolder
    set Table = Folder.GetTable
    set Columns = Table.Columns
    Columns.RemoveAll
    Columns.Add("Size")
    Table.MoveToStart
    MsgSize = 0
    set Row = Table.GetNextRow
    while not (Row Is Nothing)
      Value = Row.Item("Size")
      MsgSize = MsgSize + VAlue
      set Row = Table.GetNextRow
    wend
    MsgBox MsgSize


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

    Thursday, April 12, 2012 8:24 PM
  • Click the "Editor" button on the OutlookSpy ribbon, paste the script, click Run.

    Does it show the expected result?


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

    Thursday, April 19, 2012 4:51 PM

All replies

  • What is your code?

    Are you using multiple threads?

    What kind of message store are you using (PST, Exchange, etc.)?


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

    Wednesday, April 11, 2012 10:23 PM
  • I am spawning an asynchronous process , coz i dont want my addin to stop the start up of the outlook. The calculation of the size happens in the asynchronous process.

    Message store? we have out data files as .ost in local and we connect to a centralized server.

    Heres the code

    try

    {

    for (int i = 1; i < fItems.Count - 1; i++)

    {

    if (fItems[i] is Outlook.MailItem)

    {

    var _mail = fItems[i] as Outlook._MailItem;

    ifolderSize += _mail.Size;

    if (Marshal.IsComObject(_mail))

    {

    Marshal.ReleaseComObject(_mail);

    _mail =

    null;

    }

    Log.LogMessage(" ifolderSize: " + Convert.ToString(ifolderSize), "getOutlookFolderItemSize");

    }

    }

    }

    catch (Exception ex)

    {

    LogErrorMessage(ex);

    }


    muzammil ahmed

    Wednesday, April 11, 2012 10:38 PM
  • Outlook Object Model should not be used on a thread other than the main Outlook thread.

    Only Extended MAPI objects (C++ or Delphi) or Redemption (which is a MAPI wrapper) can be used on a secondary thread.

    That being said, there is absolutely no reason to loop through all messages in a folder and open them all just to get their size. Use the Table object (returned by MAPIFolder.GetTable) to retrieve properties from multiple messages in a single call.


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

    Wednesday, April 11, 2012 11:48 PM
  • Are you saying that the spawned thread is the cause of the exception? If so how do i prevent my code blocking the start of the outlook. We had performance issues hence i had to call one method which calculates the size as a asynchronous call back delegate.

    Can you please point me to some example code that would demonstrate the usage of table object?

    One more point that i forgot to mention is that we are getting the exception that i have mentioned only for a few users. we have deployed this addin to around 4000 users in our organization. Not all users are facing the problem.

    Thank you for all the help


    muzammil ahmed


    Thursday, April 12, 2012 4:20 AM
  • That's the problem with using Outlook objects in secondary threads: they might appear to work normal, but can and do fail at the worst possible moment.

    You can use something like the following to calculate the message size:

    set Folder = Application.ActiveExplorer.CurrentFolder
    set Table = Folder.GetTable
    set Columns = Table.Columns
    Columns.RemoveAll
    Columns.Add("Size")
    Table.MoveToStart
    MsgSize = 0
    set Row = Table.GetNextRow
    while not (Row Is Nothing)
      Value = Row.Item("Size")
      MsgSize = MsgSize + VAlue
      set Row = Table.GetNextRow
    wend
    MsgBox MsgSize


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

    Thursday, April 12, 2012 8:24 PM
  • I implemented your suggestion, but the "table" object is not returning me any rows

     private int setZone2WorkFolderSize()
            {
                Log.LogMessage("setZone2WorkFolderSize - start", "setZone2WorkFolderSize");
                Outlook.Folder zone2WorkFolder = null;

                try
                {
                    var managedFolder = oApp.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderManagedEmail) as Outlook.Folder;
                    Log.LogMessage("Managed Folder :" + managedFolder.Name.ToString(), "setZone2WorkFolderSize");

                    if (managedFolder != null)
                    {
                        string zone2WorkFolderPath = Path.Combine(managedFolder.FolderPath, "Zone2: Workspace");

                        zone2WorkFolder = MFToolHelper.GetFolderByFolderPath(managedFolder, zone2WorkFolderPath);
                        ZoneFolderTemp = zone2WorkFolder;
                        if (zone2WorkFolder != null)
                        {
                            var table = zone2WorkFolder.GetTable();
                            var columns = table.Columns;
                            columns.RemoveAll();
                            columns.Add("size");
                          
                            table.MoveToStart();
                            int msgSize = 0;
                            var row = table.GetNextRow();
                           
                            while (row != null)
                            {
                                string[] value = (string[]) row.GetValues();
                                msgSize = Convert.ToInt32(value[0]);
                                row = table.GetNextRow();
                            }

                            this.Zone2FolderSize = msgSize;
                            Log.LogMessage("zone2WorkFolder =" + zone2WorkFolder.ToString() + " | Zone2FolderSize=" + this.Zone2FolderSize.ToString(), "setZone2WorkFolderSize");
                        }
                    }
                }
                catch (Exception ex)
                {
                    MFToolHelper.LogErrorMessage(ex);
                    Log.LogException(ex, "setZone2WorkFolderSize");
                }
                finally
                {
                    if (zone2WorkFolder != null)
                    {
                        if (Marshal.IsComObject(zone2WorkFolder))
                        {
                            Marshal.ReleaseComObject(zone2WorkFolder);
                            zone2WorkFolder = null;
                        }
                    }

                    if (managedFolder != null)
                    {
                        if (Marshal.IsComObject(managedFolder))
                        {
                            Marshal.ReleaseComObject(managedFolder);
                            managedFolder = null;
                        }
                    }
                }

                Log.LogMessage("setZone2WorkFolderSize - end", "setZone2WorkFolderSize");
                return 0;
               
            }


    muzammil ahmed

    Wednesday, April 18, 2012 9:22 PM
  • Are you saying the very first call to GetNextRow returns null?

    Does my script work for you in OutlookSpy?


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

    Wednesday, April 18, 2012 10:30 PM
  • Yes GetNextRow returns null.

    Well i instaled outlook spy, but dont know what to do with it.


    muzammil ahmed

    Thursday, April 19, 2012 3:47 PM
  • Click the "Editor" button on the OutlookSpy ribbon, paste the script, click Run.

    Does it show the expected result?


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

    Thursday, April 19, 2012 4:51 PM
  • Yes, it did show the expected result.

    This enabled me to figure out the mistakes i had done. Basically i had to access the Outlook.MAPIFolder , but i was still trying to access the normal Outlook.Folder.

    My next steps would be to deploy this code (without the asynchronous process :) ) and check the performance and also check if i am still getting the exceptions.

    Thanks


    muzammil ahmed

    Thursday, April 19, 2012 8:20 PM
  • You might want to use Table.GetArray to retrieve more than one row at a time.


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

    Friday, April 20, 2012 1:47 PM
  • Table.GetArray helped me and then i put LINQ on top of it to get the sum directly.

    But i had one more question, do you have any property that the outlook api exposes that would instantly give me the size of the mailbox. Without having to go through this hassle?


    muzammil ahmed

    Friday, April 27, 2012 8:53 PM
  • For an Exchange mailbox (cached or online), you can access the PR_MESSAGE_SIZE_EXTENDED property (DASL name is http://schemas.microsoft.com/mapi/proptag/0x0E080014) using Store.PropertyAccessor, otherwise you have no choice but to recursively loop through all folders.

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

    Friday, April 27, 2012 10:53 PM
  • Some code samples please.

    Thanks,

    Muzammil Ahmed


    muzammil ahmed

    Tuesday, May 1, 2012 5:38 PM
  • set Store = Application.ActiveExplorer.CurrentFolder.Store
    MsgSize = Store.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x0E080014")
    MsgBox MsgSize


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

    Tuesday, May 1, 2012 6:11 PM
  • Thanks,

    But on a different note i tried the MAPI table and removed the asynchronous process. Even then i get the following interop exception

     Could not complete the operation. One or more parameter values are not valid. -Could not complete the operation. One or more parameter values are not valid. -    at Microsoft.Office.Interop.Outlook.TableClass.GetArray(Int32 MaxRows)
       at ****.MFToolHelper.getFolderItemsSize(MAPIFolder zone2WorkFolder)

    Note that i am running this on a user with huge mailbox (around 1gig). If i run it on a mailbox with smaller mailbox, it works fine.

    Code:

    private static int getFolderItemsSize(Outlook.MAPIFolder zone2WorkFolder)
            {
                Log.LogMessage("setZone2WorkFolderSizeMAPI - start", "setZone2WorkFolderSizeMAPI");

                int iFolderSize = 0;

                try
                {
                    if (zone2WorkFolder != null)
                    {
                        var table = zone2WorkFolder.GetTable();
                        var columns = table.Columns;
                        columns.RemoveAll();
                        columns.Add("size");

                        int rowCount = table.GetRowCount();
                        object[,] allRows = (object[,])table.GetArray(rowCount);
                        var arrSum = (from int val in allRows select val).Sum();
                        iFolderSize = arrSum;

                        Log.LogMessage("zone2WorkFolder =" + zone2WorkFolder.ToString() + " | Zone2FolderSizeMAPI(final size) =" + arrSum.ToString(), "setZone2WorkFolderSize");

                    }
                }
                catch (Exception ex)
                {
                    MFToolHelper.LogErrorMessage(ex);
                    Log.LogException(ex, "setZone2WorkFolderSize");
                }
                finally
                {
                    if (zone2WorkFolder != null)
                    {
                        if (Marshal.IsComObject(zone2WorkFolder))
                        {
                            Marshal.ReleaseComObject(zone2WorkFolder);
                            zone2WorkFolder = null;
                        }
                    }
                }

                Log.LogMessage("setZone2WorkFolderSizeMAPI - end", "setZone2WorkFolderSizeMAPI");
                return iFolderSize;

            }


    muzammil ahmed


    Tuesday, May 1, 2012 7:35 PM
  • Exchange limits the amount of data you can request in a single call to about 32 kB.

    Request fewer rows (1000?) in a loop instead of requesting all items in the folder.


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

    Tuesday, May 1, 2012 11:55 PM