Tracking eMails in Outlook 2007 - c#

Greetings Overflowers,
I am trying to develop a VSTO/C# corporate email tracker for specially signed emails.
I am relying on:
Inspectors.NewInspector
Inspector.Close
Inspector.Activate
Inspector.Deactivate
Somehow, the Inspector events stop firing after sometime.
I register 2, 3 and 4 in the body of 1 after checking for the sign.
I tried to keep track of already registered inspectors but no hope.
Any clue ?
UPDATE: Here is a sample code. The evens OnSelect and OnOpen fires few times and then stops suddenly:
using System;
using System.Collections;
using Microsoft.Office.Interop.Outlook;
// using Microsoft.Office.Core;
namespace eMailTrackingSystem
{
public enum TrackingEvent
{
Opened, Closed, Forwarded, Deleted
}
public partial class eMTSAddIn
{
private ArrayList trackedEmails = new ArrayList();
private void InternalStartup()
{
this.Application.ActiveExplorer().SelectionChange += new ExplorerEvents_10_SelectionChangeEventHandler(OnSelect);
}
private void OnSelect()
{
Selection selection = this.Application.ActiveExplorer().Selection;
foreach (object item in selection)
{
if (item is MailItem)
{
MailItem email = (MailItem)item;
if (email.Subject == "eMTS" && !trackedEmails.Contains(email.EntryID))
{
email.Open += new ItemEvents_10_OpenEventHandler(OnOpen);
trackedEmails.Add(email.EntryID);
}
}
}
}
private void OnOpen(ref bool cancel)
{
}
private void OnClose()
{
}
}
}
Regards

Are you using Inspector Wrappers? They are essential for properly dealing with every item that is opened by a user:
Developing an Inspector Wrapper for Outlook 2010:
http://msdn.microsoft.com/en-us/library/ff973716.aspx
A similar approach can be used for monitoring the items a user has selected in the Explorer.

Related

Creating an On-send prompt in Outlook with VSTO and in C#

I have a requirement to intercept the sending of a new email. All I want to do initially is ask the user "Are you sure you want to send?" and then to either proceed with the sending or cancel it depending on their response.
I found this code snippet which looks perfect for my needs but couldn't get it to work in a Win Forms test application either in VB.Net or after trying to convert it to C#. It then occurred to me that the code may only work in a VSTO Add-in (Is this correct?).
So I then used this Walkthrough to create a VSTO Add-in in C# and made sure that it worked as described, which it does (it pumps some text into the Subject and Body of a new message).
I have tried to add the first example which is in VB.Net into the working C# example but I'm a novice and don't know enough about VSTO or C# to see where I'm going wrong.
The code compiles without errors but when run, Outlook takes a long time loading the Add-in and displays a dialogue stating:
An add-in could not be found or loaded
and then:
Could not create an instance of startup object
PromptToFile_Plug_in.ThisAddIn in assembly PromptToFile Plug-in,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
Where am I going wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Windows.Forms;
namespace PromptToFile_Plug_in
{
public partial class ThisAddIn
{
Outlook.Inspectors inspectors;
Outlook.Application myOlApp = new Outlook.Application();
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors = this.Application.Inspectors;
inspectors.NewInspector +=
new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
// Note: Outlook no longer raises this event. If you have code that
// must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
}
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
Outlook.MailItem mailItem = Inspector.CurrentItem as Outlook.MailItem;
if (mailItem != null)
{
if (mailItem.EntryID == null)
{
mailItem.Subject = "This text was added by using code";
mailItem.Body = "This text was added by using code";
}
}
}
private void Initialize_handler()
{
myOlApp = this.Application;
}
private void myOlApp_ItemSend(object Item, bool Cancel)
{
string prompt;
// prompt = "Are you sure you want to send " + Item.Subject + "?";
prompt = "Are you sure you want to send?";
MessageBox.Show(prompt, "Prompt to File", MessageBoxButtons.OKCancel);
//if (MessageBox.(prompt, Constants.vbYesNo + Constants.vbQuestion, "Sample") == Constants.vbNo)
// Cancel = true;
}
#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
}
}
First of all, there is no need to create a new Outlook Application instance in the add-in class:
Outlook.Application myOlApp = new Outlook.Application();
Instead, use the Application property available in your add-in class.
Second, there is no need to keep the following method because it will never be called:
private void Initialize_handler()
{
myOlApp = this.Application;
}
Unlike VBA, you can't subscribe to the events just declaring the source objects with keywords. Instead, you need to subscribe to the events in the code like you did for the NewInspector event. For example, the following code can be used for handling the ItemSend event in VSTO add-ins:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
void Application_ItemSend(object Item, ref bool Cancel)
{
if (Item is Outlook.MailItem)
{
Outlook.MailItem mail = (Outlook.MailItem)Item;
// your code goes here
}
}
Finally, pay attention to the ref attribute for the Cancel parameter in the event handler signature.

Outlook ItemAdd event not work constantly

I have a tiny C# program which listen to the outlook with ItemAdd event, every time I start the program, it will malfunction after a few days run, for example I start the program on Monday, it will be failure on Thursday, or Wednesday, then it has to be restarted.
What's going on behind the scene? And how to fix it?
This should not be RAM issue? Because I have 32G RAM.
Win10 21H1, Outlook 2016, .net framework 4.7.2
=========================================
Problem solved, I give up the COM-way handling and adopt MailKit, things are getting straight and clear.
public partial class Form2 : Form
{
private NameSpace _ns;
private readonly ApplicationClass _outlook = new ApplicationClass();
private readonly StringProcessor _processor = new StringProcessor();
private Items _items;
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
try
{
Main();
}
catch (System.Exception ex)
{
}
}
private void Main()
{
try
{
_ns = _outlook.GetNamespace("MAPI");
MAPIFolder myFolder;
if (Environment.UserName == XmlService.UserName)
{
var box = XmlService.Inbox;
myFolder = GetFolderItem(box);
}
else
{
myFolder = _ns.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
}
if (myFolder != null)
{
_items = myFolder.Items;
_items.ItemAdd += Items_ItemAdd;
}
else
{
Loger.PrintLog("Can not get folder");
}
}
catch (System.Exception e)
{
Loger.PrintLog(e.Message);
}
}
private void Items_ItemAdd(object item)
{}
}
Keep in mind that folder based events in Outlook were only designed for the UI purposes. The events can be dropped under heavy loads; they can also be disconnected if there was an RPC failure, especially in the online (as opposed to cached) mode.
The events should at best be a hint that you code that processes new events needs to run sooner rather than later.
it will mulfunction after a few days run
Most probably the Outlook process was closed automatically. Try to create an Outlook window by using the Explorers.Add or Inspectors.Add method which creates a new instance of the explorer or inspector window and keep them alive in the code like you do for the Items collection. Note, without calling the Display method any Outlook UI will not be visible.

How do I create an Outlook add-in that alerts the user when they attempt to reply to senders that are outside of their domain?

I am new to coding in c#. I am currently trying to create an Outlook add-in to prompt and alert users if they are to attempt to reply to anyone that is not from "#abc.com". For example if 'ben#abc.com' is to trying to reply to 'jack#def.com', a window to alert Ben will be prompted warning him "You are about to reply to someone that is not from '#abc.com'." with the options of 'ok' and 'cancel'.
I referred online for the code below but this add-in only allows me to edit the field values of the email I am trying to send. I am unable to even figure out how to address and implement the code to deal with replying. I have tried researching online and have seen methods like .reply() but I am confused as to how to apply them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
namespace FirstOutlookAddIn
{
public partial class ThisAddIn
{
Outlook.Inspectors inspectors;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors = this.Application.Inspectors;
inspectors.NewInspector +=
new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
Outlook.MailItem mailItem = Inspector.CurrentItem as Outlook.MailItem;
if (mailItem != null)
{
if (mailItem.EntryID == null)
{
mailItem.To = "Testing for Recipient.";
mailItem.Subject = "Currently testing add-in for Subject.";
mailItem.Body = "Currently testing add-in for Body.";
mailItem.CC = "Testing for CC.";
}
}
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
// Note: Outlook no longer raises this event. If you have code that
// must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
}
#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
}
}
Firstly, you are only tracking Inspectors.NewInspector event. But in most cases replies will be inline - you also need Explorer.InlineResponse event (where Explorer comes from Application.ActiveExplorer, which can be null on startup, so you'd also need Application.Explorers.NewExplorer event).
Secondly, you will need to loop through all recipients in the mailItem.Recipients collection, and for each Recipient, check the Recipient.Address property (which you can test for the match).
Listen to send event, as it will be triggered irrespective of inline replies or inspector level replies.
this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
then implementation of this can be done as below,
/// param : item -> mail item to be sent
/// param : cancel -> allows you to cancel sending mail in outlook. By default value is false.
private void Application_ItemSend(object item, ref bool cancel){
Outlook.MailItem mail = (Outlook.MailItem)item;
// read "mail.To" and check for your logic;
// if To list has other domains, then show the warning prompt maybe like below,
var result = MessageBox.Show("yourmessage","title",MessageBoxButtons.YesNo);
if(result == DialogResult.No)
cancel = true; // setting this to `true` will stop sending an email out.
}
you can read on this event in MS blog here Item_Send

C#: Running multiple requests synchronously

I am trying to get multiple browsers to run tasks at the same time. I created a sort of test application just to see if I could do it as a task to myself. I was doing well but it seems like its slowed down a lot and that its waiting for the first browser to finish before the second one loads the page.
Before anyone suggests using another other than WebBrowser I would just like to say that my decision to chose a WebBrowser has good reasons.
I have a class called "Slave" to run a request to Google, I create them using the code shown below:
public void LoadSlaves()
{
for (int i = Program.GetServer().GetConfigHandler().GetValueByKeyInt("slaves_count"); i > 0; i--)
{
_slaves.Add(_slaves.Count, new Slave());
}
Logger.Warn("Loaded " + _slaves.Count + " slaves.");
}
public void StartSlaves()
{
foreach (var slave in _slaves.Values)
{
slave.Start();
}
}
Class:
using NLog;
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test.Root.Base.Slaves
{
class Slave
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
private Thread _slaveThread;
private WebBrowser _browser;
public Slave()
{
}
public void Start()
{
_slaveThread = new Thread(new ThreadStart(OnCycle));
_slaveThread.SetApartmentState(ApartmentState.STA);
_slaveThread.Start();
}
public void OnCycle()
{
while (true)
{
if (_browser == null)
{
_browser = new WebBrowser();
_browser.ScriptErrorsSuppressed = true;
_browser.DocumentCompleted += browser_DocumentCompleted;
_browser.Navigate("http://google.com");
Application.Run();
}
}
}
private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (_browser.Url == e.Url)
{
Logger.Trace("Navigated to {0} on " + Thread.CurrentThread.ManagedThreadId, e.Url);
_browser.Navigate("http://google.com");
}
}
}
}
Heres some logging:
22:17:56 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=f0KfWZ29L8HHXo_vm_gI&gws_rd=ssl on 7
22:17:58 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=gkKfWdzoAcjHXpyEl2g&gws_rd=ssl on 8
22:18:01 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=g0KfWaffJMjHXpyEl2g&gws_rd=ssl on 7
22:18:02 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=hUKfWd33KMjHXpyEl2g&gws_rd=ssl on 8
22:18:06 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=iUKfWf6xE8jHXpyEl2g&gws_rd=ssl on 8
22:18:07 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=iEKfWfPPFMjHXpyEl2g&gws_rd=ssl on 7
22:18:10 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=jUKfWcHGLMjHXpyEl2g&gws_rd=ssl on 7
22:18:12 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=jUKfWdbLB8jHXpyEl2g&gws_rd=ssl on 8
22:18:15 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=kEKfWezHPMjHXpyEl2g&gws_rd=ssl on 7
22:18:16 - Navigated to https://www.google.co.uk/?gfe_rd=cr&ei=kkKfWe35OcjHXpyEl2g&gws_rd=ssl on 8
As you can see its waiting a few seconds between requests (2-3 seconds usually), how can I get both browsers to run at the same time synchronously?
Could you be getting slowdowns because the thread started never closes. It doesn't look like you need it anymore yet it's still looping. I haven't used WebBrowser before but, with any thread that your done with you should let it finish gracefully unless there is a good reason not to.
Would something like this work for you?
...
public void OnCycle()
{
while (_browser == null)
{
...
Also just for clarification is it only getting slow with a lot of _slaves running?

How can I determine in ItemSend event handler that mail item is a reply?

I have such a class:
using Office = Microsoft.Office.Core;
using Outlook = Microsoft.Office.Interop.Outlook;
public partial class ThisAddIn
{
private void OnItemSend(object Item, ref bool Cancel)
{
Outlook.MailItem mailItem = Item as Outlook.MailItem;
}
private void OnNewInspector(Outlook.Inspector Inspector)
{
((Outlook.ItemEvents_10_Event)mailItem).Reply += new Outlook.ItemEvents_10_ReplyEventHandler(OnMailItemReply);
}
private void OnMailItemReply(object Response, ref bool Cancel)
{
var replyItem = Response as Outlook.MailItem;
// Maybe I need to add or change in replyItem some property??
}
}
I need to determine in OnItemSend event handler that mailItem is a reply message. Also I need to get there some value that I can add in OnMailItemReply method.
You can either
Check the subject if it includes a RE or FW prefix.
Read the ConversationIndex property - if its length is 44 (22 bytes on Extended MAPI level), you have a first message in a conversation. If it is greater than that, you have a reply/forward. See Tracking Conversations on MSDN

Categories