Prevent MailItem.Reply from opening Inspector window in Outlook 2016 - c#

I am building a addin for Outlookto add custom signature in outlook email, when the user click on "Reply" button.
As soon as user click on reply button in explorer, a new inspector opens and i close the inspector with code (which s#cks). There may be a way to totally disable the reply inspector from opening.
private Microsoft.Office.Tools.CustomTaskPane CustomTaskPane;
UserDetail usr = null;
Outlook.Explorer currentExplorer = null;
Outlook.MailItem mailItem;
private Inspector replyInspector;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
currentExplorer = this.Application.ActiveExplorer();
currentExplorer.SelectionChange +=
new Outlook.ExplorerEvents_10_SelectionChangeEventHandler(CurrentExplorer_Event);
}
private void CurrentExplorer_Event()
{
if (this.Application.ActiveExplorer().Selection.Count == 1
&& this.Application.ActiveExplorer().Selection[1] is Outlook.MailItem)
{
if (mailItem != null)
{
// when the reply button is clicked
((Outlook.ItemEvents_10_Event)mailItem).Reply -= new Outlook.ItemEvents_10_ReplyEventHandler(MailItem_Reply);
// When an item is selected
Outlook.Selection mySelection = this.Application.ActiveExplorer().Selection;
Outlook.MailItem mailitem = null;
foreach (Object obj in mySelection)
{
if (obj is Outlook.MailItem)
{
mailitem = (Outlook.MailItem)obj;
if (mailitem != null)
{
if (mailitem.Sent)
{
else
{
// Compose
}
}
}
}
}
mailItem = this.Application.ActiveExplorer().Selection[1];
((Outlook.ItemEvents_10_Event)mailItem).Reply += new
Outlook.ItemEvents_10_ReplyEventHandler(MailItem_Reply);
// Close Inspector
replyInspector.Close(OlInspectorClose.olDiscard);
}
else
{
}
}
void MailItem_Reply(Object response, ref bool cancel)
{
try
{
MailItem mitem = (Outlook.MailItem)response;
replyInspector = (mitem).GetInspector;
replyInspector.Activate();
(mitem).HTMLBody = tempSignature + ((Outlook.MailItem)response).HTMLBody;
}
catch (System.Exception e)
{
MessageBox.Show(e.ToString());
}
}

Firstly, why would you want to close the inspector if you are opening it anyway? Why not insert the signature when the inspector is opened?
Secondly, you cannot concatenate two HTML strings and expect the resulting string to be valid HTML, The two needs to be merged. Or, better yet, use Word Object Model (Word.Document is returned from Inspector.WordEditor) to insert any text anywhere in the message body.

Related

VSTO Outlook Add-in Event Handers not firing on start up

enter code hereI have an VSTO add in that is intercepting custom events. I have event handlers. I am looking for Add, Edit and Delete Items on the default calendar folder in outlook.
I have a button for creating custom appointments and after that is pressed the event handlers work fine but I need the event handlers to function from start up to catch custom events created in the previous session and they are not always firing at start up.
Here is some example code.
public partial class ThisAddIn
{
public string subjectName;
public Outlook.AppointmentItem apptItem;
public Outlook.Folder calendarFolder;
Outlook.Items items;
bool m_IsFirstTime = false;
CalendarMonitor calendarMonitor;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
calendarMonitor = new CalendarMonitor(Application.ActiveExplorer());
calendarMonitor.AppointmentAdded += new EventHandler<EventArgs<AppointmentItem>>(Monitor_AppointmentAdded);
calendarMonitor.AppointmentModified += new EventHandler<EventArgs<AppointmentItem>>(Monitor_AppointmentModified);
calendarMonitor.AppointmentDeleting += new EventHandler<CancelEventArgs<AppointmentItem>>(Monitor_AppointmentDeleting);
Application.Inspectors.NewInspector += Inspectors_NewInspector;
Outlook.NameSpace ns = Application.Session;
calendarFolder = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar) as Outlook.Folder;
items = calendarFolder.Items;
items.ItemChange += new Microsoft.Office.Interop.Outlook.ItemsEvents_ItemChangeEventHandler(Items_ItemChange);
}
}
//Calendar Monitor class
public class CalendarMonitor
{
private Explorer m_explorer;
private List<string> m_folderPaths;
private List<MAPIFolder> m_calendarFolders;
private List<Items> m_calendarItems;
private MAPIFolder m_deletedItemsFolder;
public event EventHandler<EventArgs<AppointmentItem>> AppointmentAdded;
public event EventHandler<EventArgs<AppointmentItem>> AppointmentModified;
public event EventHandler<CancelEventArgs<AppointmentItem>> AppointmentDeleting;
public CalendarMonitor(Explorer anExplorer)
{
m_folderPaths = new List<string>();
m_calendarFolders = new List<MAPIFolder>();
m_calendarItems = new List<Items>();
m_explorer = anExplorer;
m_explorer.BeforeFolderSwitch +=
new ExplorerEvents_10_BeforeFolderSwitchEventHandler(Explorer_BeforeFolderSwitch);
NameSpace session = m_explorer.Session;
try
{
m_deletedItemsFolder = session.GetDefaultFolder(OlDefaultFolders.olFolderDeletedItems);
HookupDefaultCalendarEvents(session);
}
finally
{
Marshal.ReleaseComObject(session);
session = null;
}
}
private void HookupDefaultCalendarEvents(NameSpace aSession)
{
MAPIFolder folder = aSession.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);
if (folder != null)
{
try
{
HookupCalendarEvents(folder);
}
finally
{
Marshal.ReleaseComObject(folder);
folder = null;
}
}
}
private void Explorer_BeforeFolderSwitch(object aNewFolder, ref bool Cancel)
{
MAPIFolder folder = (aNewFolder as MAPIFolder);
//
// Hookup events to any other Calendar folder opened.
//
if (folder != null)
{
try
{
if (folder.DefaultItemType == OlItemType.olAppointmentItem)
{
HookupCalendarEvents(folder);
}
}
finally
{
Marshal.ReleaseComObject(folder);
folder = null;
}
}
}
private void HookupCalendarEvents(MAPIFolder aCalendarFolder)
{
if (aCalendarFolder.DefaultItemType != OlItemType.olAppointmentItem)
{
throw new ArgumentException("The MAPIFolder must use " +
"AppointmentItems as the default type.");
}
//
// Ignore other user's calendars.
//
if ((m_folderPaths.Contains(aCalendarFolder.FolderPath) == false)
&& (IsUsersCalendar(aCalendarFolder)))
{
Items items = aCalendarFolder.Items;
//
// Store folder path to prevent double ups on our listeners.
//
m_folderPaths.Add(aCalendarFolder.FolderPath);
//
// Store a reference to the folder and to the items collection so that it remains alive for
// as long as we want. This keeps the ref count up on the underlying COM object and prevents
// it from being intermittently released (then the events don't get fired).
//
m_calendarFolders.Add(aCalendarFolder);
m_calendarItems.Add(items);
//
// Add listeners for the events we need.
//
((MAPIFolderEvents_12_Event)aCalendarFolder).BeforeItemMove +=
new MAPIFolderEvents_12_BeforeItemMoveEventHandler(Calendar_BeforeItemMove);
items.ItemChange += new ItemsEvents_ItemChangeEventHandler(CalendarItems_ItemChange);
items.ItemAdd += new ItemsEvents_ItemAddEventHandler(CalendarItems_ItemAdd);
}
}
private void CalendarItems_ItemAdd(object anItem)
{
AppointmentItem appointment = (anItem as AppointmentItem);
if (appointment != null)
{
try
{
if (this.AppointmentAdded != null)
{
this.AppointmentAdded(this, new EventArgs<AppointmentItem>(appointment));
}
}
finally
{
Marshal.ReleaseComObject(appointment);
appointment = null;
}
}
}
private void CalendarItems_ItemChange(object anItem)
{
AppointmentItem appointment = (anItem as AppointmentItem);
if (appointment != null)
{
try
{
if (this.AppointmentModified != null)
{
this.AppointmentModified(this, new EventArgs<AppointmentItem>(appointment));
}
}
finally
{
Marshal.ReleaseComObject(appointment);
appointment = null;
}
}
}
private void Calendar_BeforeItemMove(object anItem, MAPIFolder aMoveToFolder, ref bool Cancel)
{
if ((aMoveToFolder == null) || (IsDeletedItemsFolder(aMoveToFolder)))
{
AppointmentItem appointment = (anItem as AppointmentItem);
if (appointment != null)
{
try
{
if (this.AppointmentDeleting != null)
{
//
// Listeners to the AppointmentDeleting event can cancel the move operation if moving
// to the deleted items folder.
//
CancelEventArgs<AppointmentItem> args = new CancelEventArgs<AppointmentItem>(appointment);
this.AppointmentDeleting(this, args);
Cancel = args.Cancel;
}
}
finally
{
Marshal.ReleaseComObject(appointment);
appointment = null;
}
}
}
}
private bool IsUsersCalendar(MAPIFolder aFolder)
{
return (aFolder.Store != null);
}
private bool IsDeletedItemsFolder(MAPIFolder aFolder)
{
return (aFolder.EntryID == m_deletedItemsFolder.EntryID);
}
}
You need to keep the source objects alive if you want to get events fired correctly:
Application.Inspectors.NewInspector += Inspectors_NewInspector;
Instead, declare the Inspectors instance at the class level:
Application.Inspectors inspectors = null;
// in the method you could use the following code
inspectors = Application.Inspectors;
inspectors.NewInspector += Inspectors_NewInspector;
Define the object (Inspectors) at the class level to prevent it from swiping by the garbage collector.
You may find the Implement a wrapper for inspectors and track item-level events in each inspector article helpful.
For example, the following code is a good sample to explain a possible issue:
private void HookupCalendarEvents(MAPIFolder aCalendarFolder)
{
if (aCalendarFolder.DefaultItemType != OlItemType.olAppointmentItem)
{
throw new ArgumentException("The MAPIFolder must use " +
"AppointmentItems as the default type.");
}
//
// Ignore other user's calendars.
//
if ((m_folderPaths.Contains(aCalendarFolder.FolderPath) == false)
&& (IsUsersCalendar(aCalendarFolder)))
{
Items items = aCalendarFolder.Items;
//
// Store folder path to prevent double ups on our listeners.
//
m_folderPaths.Add(aCalendarFolder.FolderPath);
//
// Store a reference to the folder and to the items collection so that it remains alive for
// as long as we want. This keeps the ref count up on the underlying COM object and prevents
// it from being intermittently released (then the events don't get fired).
//
m_calendarFolders.Add(aCalendarFolder);
m_calendarItems.Add(items);
//
// Add listeners for the events we need.
//
((MAPIFolderEvents_12_Event)aCalendarFolder).BeforeItemMove +=
new MAPIFolderEvents_12_BeforeItemMoveEventHandler(Calendar_BeforeItemMove);
items.ItemChange += new ItemsEvents_ItemChangeEventHandler(CalendarItems_ItemChange);
items.ItemAdd += new ItemsEvents_ItemAddEventHandler(CalendarItems_ItemAdd);
}
}
The Items object is defined inside the method, so when the GC triggers the object can be swiped from the heap (if method finished its work). So, you will never get events fired after that. To prevent this from happening you need to declare the source object at the class level:
Items items = null;
private void HookupCalendarEvents(MAPIFolder aCalendarFolder)
{
if (aCalendarFolder.DefaultItemType != OlItemType.olAppointmentItem)
{
throw new ArgumentException("The MAPIFolder must use " +
"AppointmentItems as the default type.");
}
//
// Ignore other user's calendars.
//
if ((m_folderPaths.Contains(aCalendarFolder.FolderPath) == false)
&& (IsUsersCalendar(aCalendarFolder)))
{
items = aCalendarFolder.Items;
//
// Store folder path to prevent double ups on our listeners.
//
m_folderPaths.Add(aCalendarFolder.FolderPath);
//
// Store a reference to the folder and to the items collection so that it remains alive for
// as long as we want. This keeps the ref count up on the underlying COM object and prevents
// it from being intermittently released (then the events don't get fired).
//
m_calendarFolders.Add(aCalendarFolder);
m_calendarItems.Add(items);
//
// Add listeners for the events we need.
//
((MAPIFolderEvents_12_Event)aCalendarFolder).BeforeItemMove +=
new MAPIFolderEvents_12_BeforeItemMoveEventHandler(Calendar_BeforeItemMove);
items.ItemChange += new ItemsEvents_ItemChangeEventHandler(CalendarItems_ItemChange);
items.ItemAdd += new ItemsEvents_ItemAddEventHandler(CalendarItems_ItemAdd);
}
}

Outlook 2010 Addin - change phrase to link without altering email body

I've created simple addin for Outlook 2010 that replace specific phrase with link, for example I want to replace ABC12345 with http://www.example.com?id=123456
I'm using this code:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors = this.Application.Inspectors;
inspectors.NewInspector += new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}
void Inspectors_NewInspector(Outlook.Inspector Inspector)
{
var mailItem = Inspector.CurrentItem as Outlook.MailItem;
if (mailItem != null)
{
if (mailItem.EntryID != null)
{
string pattern = #"ABC(?<number>\d{1,6})";
var match = Regex.Match(mailItem.HTMLBody, pattern);
if (match.Success)
{
var replaced = Regex.Replace(mailItem.HTMLBody, pattern, m => "ABC"+ m.Groups["number"].Value+"");
mailItem.HTMLBody = replaced;
}
}
}
}
However I have two issues:
I must double click email in mailbox to have my event fired. I want to display that link when previewing emails (moving up and down in mailbox)
My code is replacing content, so Outlook "thinks" I've made some changes and when I want to close window it asks if I want to save those changes.
I don't want to alter body of email, I want to change phrase ABC123456 into link.
EDIT1:
I've tried using ActiveExplorer, this allows me to access email in preview window, but when I change email body those changes are saved. My intention is to change how specific text is displayed (change it to link), but not to alter email body on serwer.
I want those changes to be visible in my instance of Outlook, not saved on server (mailItem.HTMLBody.Replace does that).
Can I access view that displays email and alter that view?
This is the code:
Outlook.Explorer currentExplorer;
private void ThisAddIn_Startup(object sender, EventArgs e)
{
currentExplorer = this.Application.ActiveExplorer();
currentExplorer.SelectionChange += new Outlook.ExplorerEvents_10_SelectionChangeEventHandler(CurrentExplorer_Event);
}
private void CurrentExplorer_Event()
{
var selectedFolder = this.Application.ActiveExplorer().CurrentFolder;
try
{
if (this.Application.ActiveExplorer().Selection.Count > 0)
{
var selObject = this.Application.ActiveExplorer().Selection[1];
if (selObject is Outlook.MailItem)
{
Outlook.MailItem mailItem = (selObject as Outlook.MailItem);
mailItem.HTMLBody = mailItem.HTMLBody.Replace("ABC", "DEF");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
EDIT2
I'm able to get WordEditor inside Outlook, but I can't add hyperlink.
Last think I want now is to find specific phrases in email and add hyperlink to them. Here is my current code:
Outlook.Explorer currentExplorer;
private void ThisAddIn_Startup(object sender, EventArgs e)
{
currentExplorer = this.Application.ActiveExplorer();
currentExplorer.SelectionChange += new Outlook.ExplorerEvents_10_SelectionChangeEventHandler(CurrentExplorer_Event);
}
private void CurrentExplorer_Event()
{
var selectedFolder = this.Application.ActiveExplorer().CurrentFolder;
try
{
if (this.Application.ActiveExplorer().Selection.Count > 0)
{
var selectedObject = this.Application.ActiveExplorer().Selection[1];
if (selectedObject is Outlook.MailItem)
{
var mail = (selectedObject as Outlook.MailItem);
var inspector = mail.GetInspector;
if (inspector.IsWordMail())
{
var document = (Document)inspector.WordEditor;
if (document != null)
{
var app = document.Application;
var find = document.Range().Find;
find.Text = "ABC";
while (find.Execute())
{
var rng = find.Parent as Range;
rng.Hyperlinks.Add(rng, "http://example.com/?query=" + rng.Text );
rng.Collapse(WdCollapseDirection.wdCollapseEnd);
}
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}

Outlook VSTO in ItemAdd event handler creating new mailitem always has null wordeditor

I'm trying to create an Outlook add-in that processes a certain MailItem and forwards the result on to another group of people. In order to do the processing, I believe I need to use the WordEditor in order to get the proper formatting, but when I try to access that object, it returns null. I'm able to send the mail properly when I don't access the WordEditor, so it's probably something simple I've overlooked. How can I access the WordEditor for this new MailItem?
Outlook.NameSpace outlookNameSpace;
Outlook.MAPIFolder inbox;
Outlook.Items items;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
outlookNameSpace = this.Application.GetNamespace("MAPI");
inbox = outlookNameSpace.GetDefaultFolder(
Microsoft.Office.Interop.Outlook.
OlDefaultFolders.olFolderInbox);
items = inbox.Items;
items.ItemAdd +=
new Outlook.ItemsEvents_ItemAddEventHandler(items_ItemAdd);
}
void items_ItemAdd(object Item)
{
string filter = "Filter";
if (Item is Outlook.MailItem)
{
Outlook.MailItem mail = (Outlook.MailItem)Item;
if (Item != null)
{
if (mail.MessageClass == "IPM.Note" &&
mail.Subject.ToUpper().Contains(filter.ToUpper())
&& mail.Attachments.Count == 1)
{
Outlook.Attachments attachments = mail.Attachments;
if (attachments != null)
{
Outlook.Attachment file = attachments[1];
//Process attachment...
Outlook.MailItem newMail = this.Application.CreateItem(Outlook.OlItemType.olMailItem);
Outlook.Inspector insp = newMail.GetInspector;
insp.Activate();
newMail.Subject = "Subject";
newMail.To = "email#example.com";
Word.Document emailBody = insp.WordEditor as Word.Document;
//emailBody is always null
newMail.Send();
System.Runtime.InteropServices.Marshal.ReleaseComObject(newMail);
System.Runtime.InteropServices.Marshal.ReleaseComObject(file);
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(attachments);
}
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(mail);
}
}
Set the MailIten.HTMLBody appropriately without using the Inspector object and the Word Object Model.

Get attachment from current email

I am creating a outlook 2013 add-on with C# and Visual Studio 2012 and I want to get the attachment file from the current (open) mail window when a button clicked and save it to a local directory.
Is there any example for this action?
Thank you.
First you would need to get the object of the current mail item. After that you can just loop through the .Attachments of the mail item and save them with .SaveAsFile(filePath).
var _thisApp = this.Application;
Outlook.MailItem _email;
// Get current email
if(_thisApp.ActiveWindow() is Outlook.Inspector)
{
Outlook.Inspector insp = _thisApp.ActiveWindow() as Outlook.Inspector;
_email = insp.CurrentItem as Outlook.MailItem;
}
else if(_thisApp.AcitveWindow() is Outlook.Explorer)
{
Outlook.Explorer exp = _thisApp.ActiveExplorer();
if(exp.Selection.Count > 0)
_email = exp.Selection[1] as Outlook.MailItem;
}
// Loop through the attachments
foreach(Outlook.Attachment attachment in _email.Attachments)
{
// Some other stuff
string filePath = #"C:\Saved Attachments\" + attachment.FileName;
attachment.SaveAsFile(filePath);
}
EDIT: Sample to retrieve this.Application
private Outlook.Application _thisApp;
private Outlook.Inspectors _inspectors;
// Function in ThisAddin.cs
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
_thisApp = this.Application;
_inspectors = _thisApp.Inspectors;
_inspectors.NewInspector +=
new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}

Failed to access mail object MAPI from calling function

I'm going to access Outlook MAPI folder and get mail address.Here is my method
public static string GetSenderEmailAddress(Microsoft.Office.Interop.Outlook.MailItem mapiObject)
{
Microsoft.Office.Interop.Outlook.PropertyAccessor oPA;
string propName = "http://schemas.microsoft.com/mapi/proptag/0x0065001F";
oPA = mapiObject.PropertyAccessor;
string email = oPA.GetProperty(propName).ToString();
return email;
}
When button click event called,i need to fire that method and retrieve mail address.
Button click event is here.
private void button3_Click(object sender, RibbonControlEventArgs e)
{
string mailadd = ThisAddIn.GetSenderEmailAddress(Microsoft.Office.Interop.Outlook.MailItem);
System.Windows.Forms.MessageBox.Show(mailadd);
}
Error goes here
Microsoft.Office.Interop.Outlook.MailItem is a 'type'which is not valid in the given context
This is my first addin,Does anyone know how to achieve this result?
You can use the RibbonControlEventArgs to access the Context which will provide you the MailItem instance.
private Outlook.MailItem GetMailItem(RibbonControlEventArgs e)
{
// Inspector Window
if (e.Control.Context is Outlook.Inspector)
{
Outlook.Inspector inspector = e.Control.Context as Outlook.Inspector;
if (inspector == null) return null;
if (inspector.CurrentItem is Outlook.MailItem)
return inspector.CurrentItem as Outlook.MailItem;
}
// Explorer Window
if (e.Control.Context is Outlook.Explorer)
{
Outlook.Explorer explorer = e.Control.Context as Outlook.Explorer;
if (explorer == null) return null;
Outlook.Selection selectedItems = explorer.Selection;
if (selectedItems.Count != 1) return null;
if (selectedItems[1] is Outlook.MailItem)
return selectedItems[1] as Outlook.MailItem;
}
return null;
}
You can add this method and then make use of it like so...
string mailAddress = string.Empty;
Outlook.MailItem mailItem = GetMailItem(e);
if (mailItem != null)
mailAddress = ThisAddIn.GetSenderEmailAddress(mailItem);

Categories