views:

230

answers:

2

I have the following class which is intended to return the subject line of all the emails in a folder

It is Visual Studio 2008 against Outlook 2007 running on Windows 7 64bit

using System;
using System.Windows;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Outlook;

namespace MarketingEmails
{
public class MailUtils
{

  public static string[] processMailMessages(object outlookFolder)
    // Given an Outlook folder as an object reference, return
    // a list of all the email subjects in that folder
    {

        // Set a local object from the folder passed in
        Folder theMailFolder = (Folder)outlookFolder;

        string[] listSubjects = new string[theMailFolder.Items.Count];
        int itemCount = 0;

        // Now process the MAIL items
        foreach (MailItem oItem in theMailFolder.Items)
        {
            listSubjects[itemCount] = oItem.Subject.ToString();
            itemCount++;
        }
        return listSubjects;
    }

}

}

However the code throws the exception below:

Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.Office.Interop.Outlook.MailItem'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{00063034-0000-0000-C000-000000000046}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

I understand that the error which has occurred because it is trying to process a ReportItem in the selected mailbox.

What I don't understand is why is it trying ro process non-Mail Items when I have specified:

foreach (MailItem oItem in theMailFolder.Items)

If I wanted it to process Report Item entries in the mailbox I would have written:

foreach (ReportItem oItem in theMailFolder.Items)

I would dearly like to find out if this is a bug or if it is just my mis-understanding

Regards, Nigel Ainscoe

+4  A: 

The reason why is that the collection Items contains both MailItem and ReportItem instances. Specifying MailItem on the left doesn't filter the list, it simply states what type you expect to be in the list.

What you need to do is filter on the type you want like so

foreach ( MailItem oItem in theMailFolder.Items.OfType<MailItem>()) {
  ..
}

The OfType method will only return the values within the collection which match that particular type.

JaredPar
That works a treat - thanks
Nigel Ainscoe
+1  A: 

The type declaration in the foreach loop does not filter by type - it throws an exception instead, as you've noticed.

It does this because foreach was introduced in C# 1.0, which did not supprot generics. Therefore, there is no way for the compiler to know what type is returned by the IEnumerator. (This is still true if the collection doesn't implement IEnumerable<T>). Nitpickers: I know that it's possible even in C# 1 to write a strongly typed enumerator (like List<T> does); the vast majority aren't.

Back then, if you accidentally put the wrong type in the foreach, you would prefer to have it throw an exception rather than mysteriously do nothing.

As JaredPar explained, you should use the OfType method.

SLaks
OK, that throws some light on what seemed a nonsense to me. Thanks for the illumination.
Nigel Ainscoe