How can I attach an event handler for SendAndReceive event of Contact folders/Contact Items in Outlook 2007 using VSTO AddIn? I tried using:
Application.ActiveExplorer().SyncObjects.ForEach
{
SyncObject.SyncEnd += \\Do something
}
But it is not working.
I tried
Application.ActiveExplorer().SyncObjects.AppFolders.SyncEnd += \\EventHandler
This hooks on to send/receive of all default folders..
Actually my need was a bit different but may be the same:
I wanted to be notified of the changes of a folder (appointments in my case) after a send/receive.
My first thought (and I think you are on the same track) was to check for a send/receive event and maybe get some collection of items out of it or something similar, but no such thing is available. (as is also explained in this forum post)
My second path came from the following article: I can register to the Item_Add and Item_Change (and even Item_Removed) event of a folder (whom are also triggered by the changes done by a send receive):
Some code:
// Get the folder calendar folder and subscribe to the events.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar).Items.ItemAdd += new Microsoft.Office.Interop.Outlook.ItemsEvents_ItemAddEventHandler(Items_ItemAdd);
Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar).Items.ItemChange += new Microsoft.Office.Interop.Outlook.ItemsEvents_ItemChangeEventHandler(Items_ItemChange);
}
// Do something with it.
void Items_ItemAdd(object Item)
{
logItem(Item, "Add");
}
void logItem(object Item, string Action)
{
Outlook.AppointmentItem item = Item as Outlook.AppointmentItem;
File.AppendAllText(#"e:\log.txt", string.Format("Item {0}: {1}", Action, Item));
if (item != null)
{
File.AppendAllText(#"e:\log.txt", " - Appointment: " + item.Subject);
}
}
You can hook up the mail send/receive event and then check that the mail type is a ContactItem. Here is an example of the Send event.
// hook up the event
this.Application.ItemSend += ThisApplication_SentMail;
then in your event handler you check the mail item type;
internal void ThisApplication_SentMail(object item, ref bool cancel)
{
Outlook.ContactItem contactItem = item as Outlook.ContactItem;
// mail message is not a ContactItem, so exit.
if (contactItem == null) return;
// do whatever you need to here
}
In my case, I need to trigger an event after a new email is received & after email sync so that I get a new email, or else I will not receive a new email attachment.
Below my solution may help you.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.NewMail += Application_NewMail;
}
private void Application_NewMail()
{
_currentExplorer = Application.ActiveExplorer();
_currentExplorer.Session.SyncObjects[1].SyncEnd += AppFolders_SyncEnd;
_currentExplorer.Session.SyncObjects[1].Start();
}
private void AppFolders_SyncEnd()
{
//Your enter code here
}
Related
System-Environment:
Windows 10 Pro - Version: 1909 - OS System Build: 18363.752
Microsoft Outlook 2019 MSO - Version 1808 - 32-Bit
Microsoft Exchange 2016 15.1 Build (Build 1979.3)
-- Microsoft Exchange is installed on Microsoft Server 2016
Outlook Redemption COM-Library - Version 5.22.0.5498
Issue Summary:
The application sends emails via Outlook using the Outlook-Redemption COM-Library. The class "RedemptionHandler" is our Singleton-Class which interacts with the Outlook-Redemption COM-Library. During the construction of the RedemptionHandler we create a RDOSession with a static class named RedemptionLoader and call Logon() on the RDOSession. The RDOSession is used afterwards in Initialize() to retrieve the Folders for Drafts and mails which are sent.
public static class RedemptionLoader
{
public static RDOSession new_RDOSession()
{
return (RDOSession)NewRedemptionObject(new Guid("29AB7A12-B531-450E-8F7A-EA94C2F3C05F"));
}
}
public class RedemptionHandler
{
private static RedemptionHandler instance = null;
private static readonly object padlock = new object();
private RDOSession _rdoSession;
private RDOFolder _rdoSentFolder;
private RDOFolder _rdoDraftsFolder;
private RDOItems _sentItems = null;
public EventHandler<MailGesendetEventArgs> MailSuccessfullySent;
private RedemptionHandler()
{
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon(null, null, false, null, null, null);
Initialize();
}
public static RedemptionHandler Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new RedemptionHandler();
}
return instance;
}
}
}
private void Initialize()
{
try
{
if (isInitialized) return;
_rdoSentFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderSentMail);
_sentItems = _rdoSentFolder.Items;
_sentItems.ItemAdd += MailSent;
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderDrafts);
isInitialized = true;
}
catch
{
//TODO
isInitialized = false;
}
}
}
At this point, we have a working instance from our RedemptionHandler. The COM-Object RDOSession is created and referenced within just as the RDOFolder for Drafts and Sent. We have also registrered an event-listener for the Sent-Folder to recognize new Mails in this folder.
In the next steps we want to send an email and recognize this email if its stored in the sent-folder. We use the RDOMail.Fields - Property to store custom data within the RDOMail-Object.
public RDOMail CreateMail(string recipient, string subject, string body, Guid gdSender, string storagePath)
{
RDOMail newMail = _rdoDraftsFolder.Items.Add(Redemption.rdoItemType.olMailItem);
newMail.Recipients.Add(recipient);
newMail.Recipients.ResolveAll();
newMail.Subject = subject;
newMail.HTMLBody = body;
newMail.BodyFormat = (int)rdoBodyFormat.olFormatHTML;
// Here we want to store an identifier in the RDOMail.Fields
int id = newMail.GetIDsFromNames(PropertyGuid, PropertyGdItemId);
newMail.Fields[id] = Guid.NewGuid().ToString();
return newMail;
}
After the mail creation we want to display the mail to the user because we dont want to send data without letting the user know about it.
public void DisplayMail(RDOMail mail, bool modal = false)
{
mail.Display(modal, null);
}
The Outlook window now comes to front and the user checks the mail and clicks on send.
The Mail is now stored in the Sent-Folder.
The MailSent Event gets invoked by the RDOFolder.Items.Add Listener.
private void MailSent(RDOMail mail)
{
var test = mail.Fields[SenderId];
Console.WriteLine(test);
// test value is correct!
}
Difference between Exchange in Online-Mode and Cache-Mode:
If we use the Exchange with Cache-Mode, everything works fine. Everytime we send an email, the MailSent is triggered and we can read data from the RDOMail.Fields-Property. If we switch to Exchange without Cache, the MailSent Event is triggered only once, when the first mail is sent. All emails afterwars are sent but dont trigger the MailSent-Event. If we delete this line of code, everything works also fine without Cache-Mode.
var test = mail.Fields[SenderId];
This is because we think that reading data from the RDOMail.Fields - Property does something special if the cache-mode from exchange is deactivated.
We need to store custom data within the mails to check if new mails in the sent-folder are created by our application or not.
We highly appreciate help and hints.
I tried to fix this issue without success. I've set-up a new Project without any other code:
public partial class RedemptionTest : Form
{
static RDOSession _rdoSession;
static RDOFolder _rdoSentFolder;
static RDOFolder _rdoDraftsFolder;
static RDOItems _draftItems;
static RDOItems _sentItems;
public RedemptionTest()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon();
_rdoSentFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderSentMail);
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderDrafts);
_sentItems = _rdoSentFolder.Items;
_draftItems = _rdoDraftsFolder.Items;
_draftItems.ItemAdd += DraftAdd;
_sentItems.ItemAdd += MailSent;
}
private void DraftAdd(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
private void MailSent(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
}
The Drafts-Folder Event is fired all the time, the MailSent Event is only fired the first time. I have stored all RDO-Objects in static variables to avoid them from being garbage collected.
The object raising the events (RDOItems) must be alive be able to fire the events. Your code is using multiple dot notation, which means the compiler creates an implicit variable to hold the RDOItems collection. As soon as that variable is released by the Garbage Collector, no events will be fired.
The line
_rdoSentFolder.Items.ItemAdd += MailSent;
must be changed to
RDOItems _sentItems; //global/class variable
..
_sentItems = _rdoSentFolder.Items;
_sentItems .ItemAdd += MailSent;
Have the same issue in Outlook VSTO add-in using Redemption. Happens for both Sent and Inbox folder. The same code works correctly in cached mode but fires events only once in Online mode.
Native Outlook object model Items.ItemAdd works correctly in Online mode for the same folder.
Currently, we were able to do a workaround for this by unsubscribing and resubscribing to event right in the event handler. Like this:
private void SentItems_ItemAdd(RDOMail rdoMail)
{
_sentItems.ItemAdd -= SentItems_ItemAdd;
_sentItems.ItemAdd += SentItems_ItemAdd;
Log.Debug("SentItems.ItemAdd");
SentMailItemAdd?.Invoke(rdoMail);
}
I am developing an UWP Application , i want to add a Attachment to outlook from UWP app programmatically
Request you to please me know if any alternatives are there.
Looking forward for your response.
You can use the share contract to send some data to the compliant applications (including outlook). It allows you to share some text and data with any compliant apps.
To activate the sharing, you just need to register to the DataRequested event and show the share UI:
DataTransferManager.GetForCurrentView().DataRequested += OnDataRequested;
DataTransferManager.ShowShareUI();
Then, in the event handler:
private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
try
{
args.Request.Data.Properties.Title = "Share Title"
args.Request.Data.Properties.Description = "Share some data/file";
var file = await ApplicationData.Current.TemporaryFolder.GetFileAsync("myFileToShare.xxx");
args.Request.Data.SetStorageItems(new IStorageItem[] { logFile });
}
catch
{
args.Request.FailWithDisplayText("Unable to share data");
}
finally
{
deferral.Complete();
sender.DataRequested -= OnDataRequested;
}
}
Once done, the system will show the share UI where the user will be able to select the app he want. This app will receive the sent data.
While #Vincent's answer is perfect when you want to use Share Contract, if you want to use Just Email and attach the File, Below is a simple Method that i use in one of my App.
internal async void ShowEmail(string body, string subject, StorageFile attachment)
{
EmailMessage email = new EmailMessage();
email.Subject = subject;
email.Body = body;
var stream = RandomAccessStreamReference.CreateFromFile(attachment);
email.SetBodyStream(EmailMessageBodyKind.Html, stream);
await EmailManager.ShowComposeNewEmailAsync(email);
}
Above method is a strip down of the example from Here
I have written a custom code such that when ever an email arrives which has an attachment,it must get downloaded into a shared location, and email arrives daily.
When I open my laptop daily it's working fine if i don't open and if there are continuous mails (with attachments) and it is not getting downloaded for example, when i open my laptop on monday I have 3 mails with attachment (from satu, sunday, monday).
It is not downloading the latest report from monday it is still showing me same report on saturday.
Here is my code..
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);
}
private void items_ItemAdd(object Item) {
Outlook.Items inboxitems;
const string destinationDirectory=#"\\Service Now\";
Outlook.MailItem newEmail=null;
inboxitems=inbox.Items.Restrict("[Unread] = true");
try {
foreach (object collectionItem in inboxitems) {
newEmail=collectionItem as Outlook.MailItem;
if (newEmail !=null) {
if (newEmail.Attachments.Count > 0) {
for (int i=1; i <= newEmail.Attachments.Count; i++) {
if (newEmail.Attachments[i].FileName.Contains("Logic")) {
// String Des= destinationDirectory.Remove(0, 1);
newEmail.Attachments[i].SaveAsFile(destinationDirectory + newEmail.Attachments[i].FileName);
// MessageBox.Show("Hurry");
}
}
}
}
}
}catch (System.Exception ex) {
MessageBox.Show(""+ex);
}
}
Did you try to debug the code?
I see the following conditions in the code:
inboxitems= inbox.Items.Restrict("[Unread] = true");
and
if (newEmail.Attachments[i].FileName.Contains( "Logic"))
Make sure that emails corresponds to the conditions shown above.
Be aware, the ItemAdd event of the Items class is not fired when a large number of items are added to the folder at once (more than 16).
P.S. Try to check out the MarkForDownload property of Outlook items - an OlRemoteStatus value that determines the status of an item once it is received by a remote user.
I want to do some functionality when user moves a mail item (from a folder to another folder). So I want to capture the mail item move event with the outlook add-in.
I think this should be possible with following event handlers,
MAPIFolderEvents_12_BeforeItemMoveEventHandler
ItemsEvents_ItemRemoveEventHandler
I tried with both of the above event handles. But they didn’t work for me. Could someone provide an example. Here is the code for MAPIFolderEvents_12_BeforeItemMoveEventHandler.
Outlook.Folder fldr;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
fldr = (Outlook.Folder)Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderTasks);
fldr.BeforeItemMove += new Microsoft.Office.Interop.Outlook.
MAPIFolderEvents_12_BeforeItemMoveEventHandler
(Folder_BeforeItemMove);
}
private void Folder_BeforeItemMove(object anItem, MAPIFolder aMoveToFolder, ref bool Cancel)
{
Outlook.MailItem mailItem = (anItem as Outlook.MailItem);
//Do other stuff
}
The object that raises the events (fldr) must be declared on the class level instead of local to avoid being released by the Garbage Collector.
Had a similar requirement for iterating through MailFolders.
So please try as below:
Outlook.MAPIFolder mapifldr;
Outlook.Folder fldr;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
mapifldr=Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderTasks);
fldr = (Outlook.Folder) Application.GetNamespace("MAPI").GetFolderFromID(mapifldr.EntryID);
fldr.BeforeItemMove += new Microsoft.Office.Interop.Outlook.
MAPIFolderEvents_12_BeforeItemMoveEventHandler
(Folder_BeforeItemMove);
}
This is a very old post but hope it saves time for others.
I'm using the excellent MvcMailer package to send email from within my application.
I'm using the SendAsync() method to send email, and would like to log errors + dispose of attachments i.e.
MailMessage message = UserMailer.SendSomeEmail(emailViewModel);
var client = new SmtpClientWrapper();
client.SendCompleted += (sender, e) =>
{
if (e.Error != null || e.Cancelled)
{
Logger.LogError(e.Error);
}
if (message != null)
{
message.Attachments.Dispose();
message.Dispose();
}
client.Dispose();
};
message.SendAsync("this means nothing afaik", client);
This works great, but it would get too painful to repeat the same snippet all over wherver I need to send email.
How should I set this up so that I can log any errors + dispose of message attachments when the async call is completed? There has to be a better way!
If what you're trying to do is to avoid having to write that logging and cleanup code every time you send an async email, the answer is simple -- stop using anonymous methods. Just take your current code and put it in a regular method like this:
public void AsyncSendCompleted(object sender, EventArgs e)
{
// Use an appropriate type instead of EventArgs so you can get
// stuff like e.Cancelled and e.Error
// The rest of your code goes here
}
Now use this method as the event handler for your SendCompleted event:
client.SendCompleted += AsyncSendCompleted;