Ryan Muldowney

I am trying to develop an add-in for Outlook 2007 which is essentially a web browsing add-in. The basic workflow is:

1. Select a folder listed under Inbox\Mail Folders

2. A list of websites appear as mail items would

3. Select one of the mail items to pull up the site in the right frame

The purpose of this is to be able to navigate a portal system from within Outlook. It seems pretty straightforward to me but so far I've ran into a couple roadblocks. First, it seems that the WebViewURL property only exists on Folders, not on MailItems. Secondly, the only method I can find to open an HTML page from a MailItem seems to only work with HTML files saved locally, not with URLs.

As an alternative, I would be happy with having the "websites" be represented by additional folders and just using the WebViewURL property, but I don't think I can change the icon to something that doesn't resemble a folder in some way. Also, I don't think you can change the way the folders are sorted -- they are always alphabetical.

I assume that it's possible to do what I need to do, but my experience with VSTO is limited to just this project. I am hoping that someone here can lead me in the right direction... I'm not looking for a complete solution... just any suggestions that may help me get there.

Thanks,

Ryan



Re: Visual Studio Tools for Office Outlook 2007 Web Browsing Add-in

X4U

Hello Ryan,

Navigating to a Website within Outlook:

When you go to the Menu / View / Toolbars - make sure that the "Web" Toolbar is visible.

Here you can paste a Url and the Right View navigates to the Url.

How can you do this programmatically

Find the ID for this Address-Textbox, create your own CommandBar, add a textbox and give it the same ID.

Here you can paste the Url and it should work.

Select a Folder -> use the Explorer.FolderChanged event, Check the Folder and Update a Pane with the specific infromation.

Hope this helps,

greets Helmut






Re: Visual Studio Tools for Office Outlook 2007 Web Browsing Add-in

Ryan Muldowney

I don't see an Explorer.FolderChanged event... I am using C#, is there an equivalent event

On the other hand, am I better off writing this in VB





Re: Visual Studio Tools for Office Outlook 2007 Web Browsing Add-in

X4U

Hello Ryan,

here is a sample codesnippet.

There you can see a FolderSwitch event of the explorer.

Also there is a samlpe how to manage multiple Explorers and Inspectors.

Hope this will give you a start.

greets, Helmut.

P.S. sometimes VB is better - sometimes C#

my personal favourite is C#

Code Snippet

using System;

using System.Windows.Forms;

using Microsoft.VisualStudio.Tools.Applications.Runtime;

using Outlook = Microsoft.Office.Interop.Outlook;

using Office = Microsoft.Office.Core;

using System.Collections;

using System.Diagnostics;

using System.Reflection;

namespace ExplorerTest

{

public partial class ThisAddIn

{

/// <summary>

/// This generic collection holds a reference to our open explorers.

/// </summary>

Hashtable _WrappedExplorers;

/// <summary>

/// This variable must be used to keep a reference to the Application Explorers collection.

/// Required to get informed about new Explorers.

/// </summary>

Outlook.Explorers _Explorers;

Hashtable _WrappedInspectors;

Outlook.Inspectors _Inspectors;

private void ThisAddIn_Startup(object sender, System.EventArgs e)

{

// initialize the List that will keep an eye on our active Outlook Explorers.

_WrappedExplorers = new Hashtable(10);

// Do we have already some explorers after startup

_Explorers = Application.Explorers;

for (int i = _Explorers.Count; i >= 1; i--)

{

// Wrap the Explorer and do some usefull with it

WrapExplorer(_Explorers[i]);

}

// remember the Outlook.Explorers (GC)

_Explorers = this.Application.Explorers;

// register for new Explorer event.

_Explorers.NewExplorer += new Outlook.ExplorersEvents_NewExplorerEventHandler(Explorers_NewExplorer);

// remember the Outlook.Inspectors object (GC)

_Inspectors = this.Application.Inspectors;

// Create a Hashtable that holds a reference to the wrapped Inspectors

_WrappedInspectors = new Hashtable();

// register for the new inspector event

_Inspectors.NewInspector += new Outlook.InspectorsEvents_NewInspectorEventHandler(_Inspectors_NewInspector);

}

private void ThisAddIn_Shutdown(object sender, System.EventArgs e)

{

// remove all references to the wrapped Inspectors.

_WrappedInspectors.Clear();

_Inspectors = null;

// remove all references to the wrapped Explorers.

_WrappedExplorers.Clear();

_Explorers = null;

}

/// <summary>

/// The new Inspector event occures whenever an outlook inspector was opened.

/// </summary>

/// <param name="Inspector">The outlook inspector object.</param>

void _Inspectors_NewInspector(Outlook.Inspector Inspector)

{

try

{

Outlook.Inspector inspector = Inspector;

WrapInspector(inspector);

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

}

}

/// <summary>

/// This method wraps an Outlook Inspector object and registers for the InspectorClosed event.

/// </summary>

/// <param name="Inspector">The Outlook Inspector Object.</param>

void WrapInspector(Outlook.Inspector Inspector)

{

// Wrap the Explorer and do some usefull with it

InspectorWrapper wrappedInspector = new InspectorWrapper(Inspector);

// register for the closed event, so we can releas it from memory

wrappedInspector.InspectorClosed += new InspectorClosedDelegate(UnwrapInspector);

_WrappedInspectors.Add(wrappedInspector.ID, wrappedInspector);

}

/// <summary>

/// This method should be called when a wrapped Outlook Inspector has been closed.

/// </summary>

/// <param name="explorerId">The unique ID of the closed explorer.</param>

void UnwrapInspector(Guid explorerId)

{

// if we have a reference to this explorer we can release it now

if (_WrappedInspectors.ContainsKey(explorerId))

{

_WrappedInspectors.Remove(explorerId);

GC.Collect();

GC.WaitForPendingFinalizers();

}

}

/// <summary>

/// This event occures en a new outlook explorer has been opened.

/// </summary>

/// <param name="Explorer">The opened Outlook Explorer object.</param>

void Explorers_NewExplorer(Outlook.Explorer Explorer)

{

// Wrap the Explorer and do some usefull with it

Outlook.Explorer activeExplorer = Application.ActiveExplorer();

WrapExplorer(activeExplorer);

}

/// <summary>

/// This method wraps an Outlook Explorer object and registers for the ExplorerClosed event.

/// </summary>

/// <param name="Explorer">The Outlook Explorer Object.</param>

void WrapExplorer(Outlook.Explorer Explorer)

{

// Wrap the Explorer and do some usefull with it

ExplorerWrapper wrappedExplorer = new ExplorerWrapper(Explorer);

// register for the closed event, so we can releas it from memory

wrappedExplorer.ExplorerClosed += new ExplorerClosedDelegate(UnwrapExplorer);

_WrappedExplorers.Add(wrappedExplorer.ID, wrappedExplorer);

}

/// <summary>

/// This method should be called when a wrapped Outlook Explorer has been closed.

/// </summary>

/// <param name="explorerId">The unique ID of the closed explorer.</param>

void UnwrapExplorer(Guid explorerId)

{

// if we have a reference to this explorer we can release it now

if (_WrappedExplorers.ContainsKey(explorerId))

{

_WrappedExplorers.Remove(explorerId);

}

}

#region VSTO generated code

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InternalStartup()

{

this.Startup += new System.EventHandler(ThisAddIn_Startup);

this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);

}

#endregion

}

#region delegates used handling explorers

/// <summary>

/// A delegate used to inform the application that an explorer has been closed.

/// </summary>

/// <param name="explorerId">The unique ID of the Explorer that has been closed.</param>

internal delegate void ExplorerClosedDelegate(Guid explorerId);

#endregion

#region ExplorerWrapper

/// <summary>

/// The ExplorerWrapper is used to wrap around an Outlook Explorer.

/// It keeps a reference to the Explorer in memory and registers for the Explorer events.

/// </summary>

internal class ExplorerWrapper

{

#region explorer wrapper events

/// <summary>

/// This event is fired from out wrapper when the wrapped Outlook Explorer has been closed.

/// </summary>

public event ExplorerClosedDelegate ExplorerClosed;

#endregion

#region explorer wrapper variables & properties

/// <summary>

/// An unique ID that can used to identify this explorer (commandbars, buttons, etc...)

/// </summary>

Guid _Id;

/// <summary>

/// An unique ID that can used to identify this explorer (commandbars, buttons, etc...)

/// </summary>

public Guid ID

{

get { return _Id; }

}

/// <summary>

/// The Outlook Explorer object used to keep a reference in memory and register for the explorer events.

/// </summary>

Outlook.ExplorerClass _Explorer;

/// <summary>

/// The Outlook Explorer object used to keep a reference in memory and register for the explorer events.

/// </summary>

public Outlook.Explorer Explorer

{

get { return _Explorer; }

}

/// <summary>

/// A Missing.Value used for COM interoperability.

/// </summary>

object _Missing = System.Reflection.Missing.Value;

#endregion

#region explorer wrapper construction and cleanup

/// <summary>

/// The construction code.

/// </summary>

/// <param name="explorer">The outlook explorer that should be wrapped.</param>

public ExplorerWrapper(Outlook.Explorer explorer)

{

try

{

_Id = Guid.NewGuid();

_Explorer = explorer as Outlook.ExplorerClass;

if (_Explorer != null)

{

_Explorer.FolderSwitch += new Microsoft.Office.Interop.Outlook.ExplorerEvents_10_FolderSwitchEventHandler(_Explorer_FolderSwitch);

_Explorer.ExplorerEvents_Event_Close += new Microsoft.Office.Interop.Outlook.ExplorerEvents_CloseEventHandler(_Explorer_ExplorerEvents_Event_Close);

}

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// throw the Exception to the calling class

throw ex;

}

}

#endregion

/// <summary>

/// This event occures when this explorer has been closed.

/// </summary>

void _Explorer_ExplorerEvents_Event_Close()

{

// here we release all references to the explorer from memory.

_Explorer.FolderSwitch -= new Microsoft.Office.Interop.Outlook.ExplorerEvents_10_FolderSwitchEventHandler(_Explorer_FolderSwitch);

_Explorer.ExplorerEvents_Event_Close -= new Microsoft.Office.Interop.Outlook.ExplorerEvents_CloseEventHandler(_Explorer_ExplorerEvents_Event_Close);

_Explorer = null;

// fire an event to inform the application that this explorer has been closed.

if (ExplorerClosed != null)

{

ExplorerClosed(this._Id);

}

}

/// <summary>

/// This event occures when another folder has been selected in this explorer.

/// </summary>

void _Explorer_FolderSwitch()

{

try

{

// We change the application menu depending on what folder we have selected

// ModifyMenu();

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// now display a friendly error to the user

MessageBox.Show(null, "There was an application error, you should save your work and restart Outlook.",

"OutlookAdminTools",

MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

}

#endregion

}

#region delegates used handling inspectors

/// <summary>

/// A delegate used to inform the application that an inspector has been closed.

/// </summary>

/// <param name="inspectorId">The unique ID of the Explorer that has been closed.</param>

internal delegate void InspectorClosedDelegate(Guid inspectorId);

#endregion

/// <summary>

/// The <b>InspectorWrapper</b> class is used to handle multiple open document windows within outlook.

/// </summary>

internal class InspectorWrapper

{

/// <summary>

/// This event informs our application that an "wrapped" inspector has been closed and should be released from memory.

/// </summary>

public event InspectorClosedDelegate InspectorClosed;

#region explorer wrapper variables & properties

/// <summary>

/// An unique ID that can used to identify this explorer (commandbars, buttons, etc...)

/// </summary>

Guid _Id;

/// <summary>

/// An unique ID that can used to identify this explorer (commandbars, buttons, etc...)

/// </summary>

public Guid ID

{

get { return _Id; }

}

/// <summary>

/// This variable holds a reference to the outlook iitem behind the inspector.

/// Used to handle to item.open event.

/// Only when the item has been displayed, it's save to manipulate the inspector - or close the inspector.

/// </summary>

object _Item;

/// <summary>

/// The outlook inspector object that is wrapped by this class.

/// </summary>

Outlook.InspectorClass _Inspector;

#endregion

/// <summary>

/// Initialisation code.

/// Remembers the inspector in memory and registers for the close event and the item.open event

/// </summary>

/// <param name="inspector">The outlook inspector object that should be handled.</param>

public InspectorWrapper(Outlook.Inspector inspector)

{

try

{

// each inspector gets a unique id.

_Id = Guid.NewGuid();

// remember the inspector object in memory

_Inspector = inspector as Outlook.InspectorClass;

// register for the close event - used to release ourself from memory

_Inspector.InspectorEvents_Event_Close += new Outlook.InspectorEvents_CloseEventHandler(_Inspector_InspectorEvents_Event_Close);

// remember the item behind the inspector in memory

_Item = _Inspector.CurrentItem;

// register for the item_open event

if (_Item is Outlook.ContactItem)

{

// we have no latebinding - so we have to cast explicitly to get events

Outlook.ContactItem item = _Item as Outlook.ContactItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

// remember the object in memory

_Item = item;

}

else if (_Item is Outlook.TaskItem)

{

Outlook.TaskItem item = _Item as Outlook.TaskItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

_Item = item;

}

else if (_Item is Outlook.JournalItem)

{

Outlook.JournalItem item = _Item as Outlook.JournalItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

_Item = item;

}

else if (_Item is Outlook.MailItem)

{

Outlook.MailItem item = _Item as Outlook.MailItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

_Item = item;

}

else

{

// we don't handle this inspector and release it from memory

_Item = null;

}

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// the wrapper is called only from application - throw the message to it.

throw new Exception("There was an application error, the new Window could not be handled.\nyou should save your work and restart Outlook.", ex);

}

}

/// <summary>

/// Handles the item open event and checks if this item a OutlooAdminTools item.

/// When we have a new item - and we are in an OAT folder - we close this item and create a new OAT item.

/// </summary>

/// <param name="Cancel"></param>

void item_Open(ref bool Cancel)

{

try

{

Type itemType = _Item.GetType();

// check the messageclass of the new opened inspector.

string messageclass = (string)itemType.InvokeMember("MessageClass", BindingFlags.GetProperty, null, _Item, null);

// access the entryId - so we can get the parent folder object

string entryId = (string)itemType.InvokeMember("EntryID", BindingFlags.GetProperty, null, _Item, null);

// after we have accessed the entryID - we can access the parentfolder property

Outlook.MAPIFolder parent = itemType.InvokeMember("Parent", BindingFlags.GetProperty, null, _Item, null) as Outlook.MAPIFolder;

string folderName = parent.Description;

int itemSize = (int)itemType.InvokeMember("Size", BindingFlags.GetProperty, null, _Item, null);

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// the wrapper is called anytime from application.

// display a messagebox directly to the user

MessageBox.Show("There was an application error, a new Window could not be handled,\nyou should save your work and restart Outlook.",

"OutlookAdminTools",

MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

}

/// <summary>

/// This event occures when the user has closed the open inspector.

/// Used to release ourself from memory and avoid memory leaks.

/// </summary>

void _Inspector_InspectorEvents_Event_Close()

{

try

{

_Item = null;

_Inspector.InspectorEvents_Event_Close -= new Outlook.InspectorEvents_CloseEventHandler(_Inspector_InspectorEvents_Event_Close);

_Inspector = null;

GC.Collect();

GC.WaitForPendingFinalizers();

if (InspectorClosed != null)

{

InspectorClosed(_Id);

}

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// the wrapper is called anytime from application.

// display a messagebox directly to the user

MessageBox.Show("There was an application error, a Window close could not be handled,\nyou should save your work and restart Outlook.",

"OutlookAdminTools",

MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

}

}