Topaz Filer - Email filing software
Accessing common properties of Outlook Items in .NET
Messages   Related Types
This message was discovered on microsoft.public.dotnet.framework.interop.
Responses highlighted in red are from those people who are likely to be able to contribute good, authoratitive information to this discussion. They include Microsoft employees, MVP's and others who IMHO contribute well to these kinds of discussions.

Mike Timms
GOOD ANSWER
I'm trying to find a clean approach to accessing the common properties of
Outlook items using the Outlook object model under .NET.

The Outlook Items collection can contain heterogeneous object types such as
MailItem, PostItem, ContactItem etc. In VB6 it was possible to declare an
item as variant and then you could access properties common to any of item
types without having to cast to a specific object type. See:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnout2k/htm
l/olcollections.asp.

However, the equivalent technique doesn't work under .NET, since there is no
base item class to inherit from. It's therefore necessary to determine the
actual item type and then cast to a variable of the specific type.
Following is a partial implementation of the code required to get the
MessageClass of an item:

Public void OnNewInspector(Outlook.Inspector inspector)
{
string messageClass;

if (inspector.CurrentItem is Outlook.MailItem)
{
Outlook.MailItem mitem = inspector.CurrentItem as Outlook.MailItem;
messageClass = mitem.MessageClass;
}
else if (inspector.CurrentItem is Outlook.PostItem)
{
Outlook.PostItem pitem = inspector.CurrentItem as Outlook.PostItem;
messageClass = pitem.MessageClass;
}
else if ......

The only mechanism I found to simplify this was to create a pseudo item
class that wraps the ugliness of testing the object type and gives the
client code at least the illusion of a base item object. A partial
implementation is:

public class OutlookItem : IDisposable
{
private Outlook.OlItemType m_itemType;
private Outlook.PostItem m_pItem;
private Outlook.MailItem m_mItem;
....

// constructor
public OutlookItem(object item)
{

if (item is Outlook.PostItem)
{
m_itemType = Outlook.OlItemType.olMailItem;
m_mItem = item as Outlook.MailItem;
}
else if (item is Outlook.PostItem)
{
m_itemType = Outlook.OlItemType.olPostItem;
m_pItem = item as Outlook.PostItem;
}
else if (item is ............)
.........
else
{
// raise invalid object exception
}
}

public Outlook.OlItemType ItemType
{
get
{ return m_itemType; }
}

public string MessageClass
{
get
{
switch (m_itemType)
{
case Outlook.OlItemType.olPostItem:
return m_pItem.MessageClass;
case Outlook.OlItemType.olMailItem:
return m_mItem.MessageClass;
case Outlook.OlItemType......
............
}
return "";
}
set
{
switch (m_itemType)
{
case Outlook.OlItemType.olPostItem:
m_pItem.MessageClass = value;
break;
case Outlook.OlItemType.olMailItem:
m_mItem.MessageClass = value;
break;
case Outlook.OlItemType......

}
}
}

public void Dispose ()
{
switch (m_itemType)
{
case Outlook.OlItemType.olPostItem:
Marshal.ReleaseComObject(m_pItem);
break;
case Outlook.OlItemType.olMailItem:
Marshal.ReleaseComObject(m_mItem);
break;
case Outlook.OlItemType......
}
}

This class can then be used as follows:

private void OnNewInspector(Outlook.Inspector inspector)
{
string messageClass;

OutlookItem item = new OutlookItem(inspector.CurrentItem);
messageClass = item.MessageClass;
if (messageClass = "IPM.Note.MyMessageClass")
{
..........
}
item.Dispose();
}

This doesn't reduce the amount of code but at least hides the ugliness in
the helper class. However, since VB6 can access these items
polymorphically, I'm hoping there's a more elegant solution using interop
library functions. Has anybody got an ideas?

- Mike

Reply to this message...
Vote that this is a GOOD answer... (3 votes from other users already)
 
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
GOOD ANSWER
Mike,
In VB.NET this is easy. You turn Option Strict Off, place the Outlook Item
in an Object variable, then call the methods.

VB.NET is using interop methods to call IDispatch methods of the Outlook
item behind the scenes for you. Unfortunately I cannot find what those
methods are, nor the reference I had to them...

FWIW: In your code example, I would use polymorphism rather then ifs &
switches...

Forgive my C# ;-)

private void OnNewInspector(Outlook.Inspector inspector)
{
string messageClass;

OutlookItem item = OutlookItem.CreateItem(inspector.CurrentItem);

messageClass = item.MessageClass;
if (messageClass = "IPM.Note.MyMessageClass")
{
..........
}
item.Dispose();
}

class OutlookItem
{
public abstract string MessageClass { get; set }

// Only this method needs if statements!
static OutlookItem CreateItem(object item)
{
if (item is Outlook.MailItem)
{
return new MailItem(item)
}
else if (item is Outlook.PostItem)
{
return new PostItem(item)
}
...
else
{
// raise invalid object exception
}
}

}

class MailItem : OutlookItem
{
Outlook.MailItem m_item;

MailItem(object item)
{
m_item = item as Outlook.MailItem;
}

public string MessageClass
{
get
{
return m_item.MessageClass;
}
set
{
m_item.MessageClass = value;
}
}
}

Of course you could use VB.NET to write the wrapper, which is then used by
your C# code...

Hope this helps
Jay

"Mike Timms" <Click here to reveal e-mail address> wrote in message
news:O8G8sB#jBHA.5836@tkmsftngp04...
[Original message clipped]

Reply to this message...
Vote that this is a GOOD answer... (2 votes from other users already)
 
 
    
Mike Timms
GOOD ANSWER
Jay,

Thanks for your response.

"Jay B. Harlow [MVP - Outlook]" <Click here to reveal e-mail address> wrote in message news:<u6KUcBVkBHA.2448@tkmsftngp02>...
[Original message clipped]

I did some more digging on accessing properties through IDispatch and
found that I can use the System.Type.InvokeMember method to access a
named property at run time regardless of the actual object type. This
was just what I was looking for!

The following C# code works:

object item = inspector.CurrentItem;
Type t = item.GetType();
string messageClass = t.InvokeMember("MessageClass",
BindingFlags.Public |
BindingFlags.GetField |
BindingFlags.GetProperty,
null,
item,
new object[]{}).ToString();
if (messageClass = "IPM.Note.MyMessageClass")
{
..........
}

I'm still intending to use the OutlookItem helper class since it makes
the client code easier to understand. But now I can greatly simplify
it and just wrap the InvokeMember method call for each property
access. A partial implementation of the helper class now looks like
the following:

public class OutlookItem : IDisposable
{
private object m_item;
private Type m_type;

// constructor
public OutlookItem(object item)
{
m_item = item;
m_type = item.GetType();
}

public string MessageClass
{
get
{
return (m_type.InvokeMember("MessageClass",         
BindingFlags.Public |
BindingFlags.GetField |
BindingFlags.GetProperty,
     null,
m_item,
new object[]{}
).ToString());
}
set
{
............
}
}

public string EntryID
{
get
{
return (m_type.InvokeMember("EntryID",         
BindingFlags.Public |
BindingFlags.GetField |
BindingFlags.GetProperty,
     null,
m_item,
new object[]{}
).ToString());
}
}
public ..... other common properties

public void Dispose ()
{
Marshal.ReleaseComObject(m_item);
}
}

thanks again,

- Mike

[Original message clipped]

Reply to this message...
Vote that this is a GOOD answer... (1 vote from another user already)
 
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
GOOD ANSWER
Mike,
Glad you found it. I knew it was there, I just did not remember where, I was
thinking it was under the Interop namespace itself...

Yes, the single helper class in this case will be very beneficial!

Thanks for the info
Jay

"Mike Timms" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]


Reply to this message...
Vote that this is a GOOD answer... (1 vote from another user already)
 
 
    
Mike Timms
GOOD ANSWER
Here's the updated OutlookItem helper class with a representative selection
of property value types implemented.

- Mike

------------------------------------
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using Outlook;

namespace OutlookLibrary
{
/// <summary>
/// Helper class to access common properties of Outlook Items.
/// </summary>
///
/// Uses the IDispatch interface of the Outlook object model to access
the common properties of
/// Outlook items regardless of the actual item type. This prevents
having to test for and cast to
/// a specific object in order to access common properties.
///
/// Usage example:
///
/// OutlookItem item = new Outlook.Item(inspector.CurrentItem);
/// string message = item.CreateTime.ToString("f") + " - " +
item.Subject);
/// MessageBox.Show(message, "New Inspector");
/// item.Dispose();
///
//
// TODO: Add remaining common properties
// TODO: Handle 'no such property name' exceptions
//

public class OutlookItem : IDisposable
{
private object m_item; // the wrappped Outlook item
private Type m_type; // type for the Outlook item
private object[] m_args; // dummy argument array
private Outlook.OlItemType m_itemType; // item type of Outlook item
private bool m_isItemTypeInitialized;
private System.Type m_typeOlObjectClass;

// constants for property names
private const string OlMessageClass = "MessageClass";
private const string OlClass = "Class";
private const string OlCreationTime = "CreationTime";
private const string OlSubject = "Subject";
private const string OlParent = "Parent";
private const string OlFormDescription = "FormDescription";
private const string OlEntryID = "EntryID";

// Constructor
public OutlookItem(object item)
{
m_item = item;
m_type = m_item.GetType();
m_args = new Object[]{};
m_isItemTypeInitialized = false;
}

public void Dispose()
{
Marshal.ReleaseComObject(m_item);
}

// Public Properties
public Outlook.OlItemType ItemType
{
get
{
// item type is evaluated in 'lazy' fashion the first time
required
if (m_isItemTypeInitialized)
return m_itemType;
else if (m_item is Outlook.PostItem)
m_itemType = Outlook.OlItemType.olPostItem;
else if (m_item is Outlook.MailItem)
m_itemType = Outlook.OlItemType.olMailItem;
else if (m_item is Outlook.ContactItem)
m_itemType = Outlook.OlItemType.olContactItem;
else if (m_item is Outlook.AppointmentItem)
m_itemType = Outlook.OlItemType.olAppointmentItem;
else if (m_item is Outlook.TaskItem)
m_itemType = Outlook.OlItemType.olTaskItem;
else if (m_item is Outlook.JournalItem)
m_itemType = Outlook.OlItemType.olJournalItem;
else if (m_item is Outlook.DistListItem)
m_itemType = Outlook.OlItemType.olDistributionListItem;
else if (m_item is Outlook.NoteItem)
m_itemType = Outlook.OlItemType.olNoteItem;
m_isItemTypeInitialized = true;
return m_itemType;
}
}

public string EntryID
{
get
{
return this.GetPropValue(OlEntryID).ToString();
}
}

public string Subject
{
get
{
return this.GetPropValue(OlSubject).ToString();
}
}

public string MessageClass
{
get
{
return this.GetPropValue(OlMessageClass).ToString();
}
}

public Outlook.OlObjectClass Class
{
get
{
if (m_typeOlObjectClass == null)
{
// Note: instantiate dummy ObjectClass enumeration to
get type.
// type =
System.Type.GetType("Outlook.OlObjectClass") doesn't seem to work
Outlook.OlObjectClass objClass =
Outlook.OlObjectClass.olAction;
m_typeOlObjectClass = objClass.GetType();
}
return
(Outlook.OlObjectClass)System.Enum.ToObject(m_typeOlObjectClass,
his.GetPropValue(OlClass));
}
}

public System.DateTime CreationTime
{
get
{
return (System.DateTime)this.GetPropValue(OlCreationTime);
}
}

public object Parent
{
get
{
return this.GetPropValue(OlParent);
}
}

public Outlook.FormDescription FormDescription
{
get
{
return this.GetPropValue(OlFormDescription) as
Outlook.FormDescription;
}
}

// Private methods

// Get a propery value by name
private object GetPropValue(string propertyName)
{
// An invalid property name exception is propagated to client
return m_type.InvokeMember(
propertyName,
BindingFlags.Public
| BindingFlags.GetField | BindingFlags.GetProperty,
null,
m_item,
m_args);
}

}

}

Reply to this message...
Vote that this is a GOOD answer... (1 vote from another user already)
 
 
 
System.DateTime
System.Enum
System.IDisposable
System.Object
System.Reflection.BindingFlags
System.Runtime.InteropServices.Marshal
System.Type
System.Windows.Forms.MessageBox




Ad
BootFX
Reliable and powerful .NET application framework.
Looking to invest in a major software project? Technical and commercial advice available here.
Other Helpful Sites
MBR 247
Topaz Filer
SharePoint Email Filing
Software Advisory Services
 
Copyright © AMX Software Ltd 2008-2010. Portions copyright © Matthew Baxter-Reynolds 2001-2010. All rights reserved.
Contact Us - Terms of Use - Privacy Policy - 4.0.30129.1734