I'm new to office addins. I'm an MVC programmer but this project has been dumped on me as no one else wants to do it. I need to create an outlook addin that will forward all email data to a service where communications can be tracked by a recruitment system.
I am using
Application.ItemSend += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_ItemSendEventHandler(saveEmail);
where I then cast the email into a Outlook.MailItem. The problem is I see no way of getting the from and to email addresses. All it gives me is the name of the people. Am I missing something?
So far the best solution I can think of is to save the msg as a .msg file. Forward that to my service and then user a parser I found to convert it to HTML.
Any suggestions?
To access the recipients, loop through MailItem.Recipients collection and access Recipient.Name and Recipient.Address properties.
Sender related properties are not yet set by the time ItemSend event fires - the earliest you can access sender properties is when Items.ItemAdd event fires on the Sent Items folder (retrieve it using Namespace.GetDefaultFolder).
You can read the MailItem.SendUsingAccount. If it is null, use the first Account from the Namespace.Acounts collection. You can then use Account.Recipient object.
Keep in mind that you should not blindly cast outgoing items to MailItem objects - you can also have MeetingItem and TaskRequestItem objects.
OK using the info given to me by Dmitry Streblechenko and some other info I just looked up here is my solution so far.
In the ItemSend event I first make sure that sent email is moved to the default sent items folder. I'm testing outlook using gmail so normally these will go elsewhere. sentMailItems is made as a class field as apparently it will get garbage collected if its just declared inside the Startup function (Something quite odd to me an MVC programmer :) ).
I' will test this on exchange when I get back to office an hopefully all goes well.
public partial class ThisAddIn
{
public Outlook.Items sentMailItems;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.ItemSend += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_ItemSendEventHandler(ItemSend);
sentMailItems = Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail).Items;
sentMailItems.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(Items_ItemAdd);
}
void Items_ItemAdd(object item)
{
MessageBox.Show(((Outlook.MailItem)item).Subject);
var msg = Item as Outlook.MailItem;
string from = msg.SenderEmailAddress;
string allRecip = "";
foreach (Outlook.Recipient recip in msg.Recipients)
{
allRecip += "," + recip.Address;
}
}
private void ItemSend(object Item, ref bool Cancel)
{
if (!(Item is Outlook.MailItem))
return;
var msg = Item as Outlook.MailItem;
msg.DeleteAfterSubmit = false; // force storage to sent items folder (ignore user options)
Outlook.Folder sentFolder = this.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail) as Outlook.Folder;
if (sentFolder != null)
msg.SaveSentMessageFolder = sentFolder; // override the default sent items location
msg.Save();
}
//Other auto gen code here....
}
Related
I have gone through many links already but none of them seems to be working. My problem is that in an Outlook Add-In written using C# and VSTO, I am looking to capture the text of the latest Reply email to a thread.
The problem is that all the properties on a MailItem object such as Body, HTMLBody etc give the entire text of the email including past replies. I am looking to somehow only get the most recent text. And I need to be able to do this considering multiple languages in an email.
Here is what i have tried
Using Bookmarks on the MailEditor - There seems to be no more _MailOriginal bookmark with exchange and Outlook
Somehow trying to get hold off MIME properties - I don't know enough on which properties to pick and how to use them to parse the recent text.
You cannot do that even in theory: imagine a user typing at the top of the message (e.g. "see below") and then inserting/deleting various pieces in the message body below (I do that all the time). You are lucky if the font color is different.
You can try to compare the original with the new and figure out the diff, but that requires access to the original message. You can look at the PR_IN_REPLY_TO_ID MAPI property (DASL name http://schemas.microsoft.com/mapi/proptag/0x1042001F) and try to find the original message either in the Inbox or the Sent Items folder. Note that in the latter case (Sent Items folder) the property might not be available on the cached message, you'd need to search the online version of the folder (cannot do that in OOM, you'd need Extended MAPI in C++ or Delphi or Redemption in any language).
The Outlook object model doesn't provide anything for that. You need to parse the message body string on your own.
Also, you can iterate over all items in the conversation and detect each of them in the latest/recent item. By removing older items you can get the latest. The following example shows how to get and display mail items in a conversation.
void DemoConversation()
{
object selectedItem = Application.ActiveExplorer().Selection[1];
// For this example, you will work only with
//MailItem. Other item types such as
//MeetingItem and PostItem can participate
//in Conversation.
if (selectedItem is Outlook.MailItem)
{
// Cast selectedItem to MailItem.
Outlook.MailItem mailItem = selectedItem as Outlook.MailItem;
// Determine store of mailItem.
Outlook.Folder folder = mailItem.Parent as Outlook.Folder;
Outlook.Store store = folder.Store;
if (store.IsConversationEnabled == true)
{
// Obtain a Conversation object.
Outlook.Conversation conv = mailItem.GetConversation();
// Check for null Conversation.
if (conv != null)
{
// Obtain Table that contains rows
// for each item in Conversation.
Outlook.Table table = conv.GetTable();
Debug.WriteLine("Conversation Items Count: " + table.GetRowCount().ToString());
Debug.WriteLine("Conversation Items from Table:");
while (!table.EndOfTable)
{
Outlook.Row nextRow = table.GetNextRow();
Debug.WriteLine(nextRow["Subject"]
+ " Modified: "
+ nextRow["LastModificationTime"]);
}
Debug.WriteLine("Conversation Items from Root:");
// Obtain root items and enumerate Conversation.
Outlook.SimpleItems simpleItems = conv.GetRootItems();
foreach (object item in simpleItems)
{
// In this example, enumerate only MailItem type.
// Other types such as PostItem or MeetingItem
// can appear in Conversation.
if (item is Outlook.MailItem)
{
Outlook.MailItem mail = item as Outlook.MailItem;
Outlook.Folder inFolder = mail.Parent as Outlook.Folder;
string msg = mail.Subject
+ " in folder " + inFolder.Name;
Debug.WriteLine(msg);
}
// Call EnumerateConversation
// to access child nodes of root items.
EnumerateConversation(item, conv);
}
}
}
}
}
void EnumerateConversation(object item, Outlook.Conversation conversation)
{
Outlook.SimpleItems items = conversation.GetChildren(item);
if (items.Count > 0)
{
foreach (object myItem in items)
{
// In this example, enumerate only MailItem type.
// Other types such as PostItem or MeetingItem
// can appear in Conversation.
if (myItem is Outlook.MailItem)
{
Outlook.MailItem mailItem = myItem as Outlook.MailItem;
Outlook.Folder inFolder = mailItem.Parent as Outlook.Folder;
string msg = mailItem.Subject
+ " in folder " + inFolder.Name;
Debug.WriteLine(msg);
}
// Continue recursion.
EnumerateConversation(myItem, conversation);
}
}
}
In the sample code example, we get a selected MailItem object and then determine the store of the MailItem object by using the Store property of the Folder object. DemoConversation then checks whether the IsConversationEnabled property is true; if it is true, the code example gets Conversation object by using the GetConversation method. If the Conversation object is not a null reference, the example gets the associated Table object that contains each item in the conversation by using the GetTable method. The example then enumerates each item in the Table and calls EnumerateConversation on each item to access the child nodes of each item. EnumerateConversation takes a Conversation object and gets the child nodes by using the GetChildren(Object) method. EnumerateConversation is called recursively until there are no more child nodes. Each conversation item is then displayed to the user.
I am editing an email and send it to the recipient. But i also want to save the original mail in the sent folder. But if i move the mailobject to the folder the mail is still editable.
This is how i move the mail:
private void CopyMailToSent(Outlook.MailItem originalMail)
{
var folder = originalMail.SaveSentMessageFolder;
originalMail.Move(folder);
}
Can i set the mailobject to readonly or faking the send?
Firstly, Outlook Object Model would not let you set the MailItem.Sent property at all. On the MAPI level, the MSGFLAG_UNSENT bit in the PR_MESSAGE_FLAGS property can only be set before the message is saved for the very first time.
The only OOM workaround I am aware of is to create a post item (it is created in the sent state), set its message class to "IPM.Note", save it, release it, reopen by the entry id (it will be now MailItem in the sent state), reset the icon using PropertyAccessor, set some sender properties (OOM won't let you set all of them).
If using Redemption (I am its author) is an option, it will let you set the Sent property as well as the sender related properties, plus add recipients without having to resolve them.
Set MySession = CreateObject("Redemption.RDOSession")
MySession.MAPIOBJECT = Application.Session.MAPIOBJECT
Set folder = MySession.GetDefaultFolder(olFolderSentMail)
Set msg = folder.Items.Add("IPM.Note")
msg.Sent = True
msg.Recipients.AddEx "Joe The User", "joe#domain.demo", "SMTP", olTo
msg.Sender = MySession.CurrentUser
msg.SentOnBehalfOf = MySession.CurrentUser
msg.subject = "Test sent message"
msg.Body = "test body"
msg.UnRead = false
msg.SentOn = Now
msg.ReceivedTime = Now
msg.Save
I couldn't solve the problem but i did workaround which works fine for me.
I hope it's ok to post this here even it's not right the solution. If not, sorry i will delete it.
My workaround is saving the orignal mail as ".msg" file and then add it to the mail in the sent folder.
Then it looks like this:
This is the code:
private void SendMail(Outlook.Mailitem mail)
{
mail.SaveAs(tempDirectory + #"originalMail.msg");
var folder = mail.SaveSentMessageFolder;
ChangeMailSubject(mail);
ChangeMailText(mail);
mail.Send();
folder.Items.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler((sender) => AttachOriginalMail(sender);
}
private void AttachOriginalMail(object sender)
{
var mail = (Outlook.MailItem) sender;
mail.Attachments.Add(tempDirectory + #"originalMail.msg");
mail.Save();
}
Hello I recently got into development around EWS. One of the issue came up to me is that a client ask me to import emails into database and he wants to detect the duplicate based on InternetMessageID this way he doesn't have to import the duplicate emails and my code came up to this point.
private static string GetInternetMessageID(Microsoft.Exchange.WebServices.Data.Item email)
{
EmailMessage emailMsg = email as EmailMessage;
string returnId = string.Empty;
if ((emailMsg != null)) {
try {
emailMsg.Load();
//loads additional info, without calling this ToRecipients (and more) is empty
} catch (ArgumentException ex) {
//retry
email.Load();
}
returnId = emailMsg.InternetMessageId;
} else {
//what to do?
}
return returnId;
}
I can handle regular emails, but for special exchange objects such as contact, Calendar, Posts etc it does not work because it could not cast it to an EmailMessage object.
And I know you can extract the internetMessageId from those objects. Because the client used to have another software that extract this ID for them, maybe the property is not called internetMessageID, I think I probally have to extract it from the internetMessageHeader. However when ever I try to get it from the item object it just throws me an error. How do I get the internet messageID from these "Special" exchange items?
PS i am aware of item.id.UniqueID however that is not what I want as this id changes if I move items from folder to another folder in exchange
Only objects that have been sent via the Transport service will have an InternetMessageId so things like Contacts and Tasks because they aren't messages and have never been routed via the Transport service will never have an Internet MessageId. You probably want to look at using a few properties to do this InternetMessageId can be useful for messages PidTagSearchKey https://msdn.microsoft.com/en-us/library/office/cc815908.aspx is one that can be used (if you good this there are various examples of using this property).
If your going to use it in Code don't use the method your using to load the property on each item this is very inefficient as it will make a separate call for each object. Because these I'd's are under 256 Kb just retrieve then when using FindItems. eg
ExtendedPropertyDefinition PidTagSearchKey = new ExtendedPropertyDefinition(0x300B, MapiPropertyType.Binary);
ExtendedPropertyDefinition PidTagInternetMessageId = new ExtendedPropertyDefinition(0x1035, MapiPropertyType.String);
PropertySet psPropSet = new PropertySet(BasePropertySet.IdOnly);
psPropSet.Add(PidTagSearchKey);
psPropSet.Add(PidTagInternetMessageId);
ItemView ItemVeiwSet = new ItemView(1000);
ItemVeiwSet.PropertySet = psPropSet;
FindItemsResults<Item> fiRess = null;
do
{
fiRess = service.FindItems(WellKnownFolderName.Inbox, ItemVeiwSet);
foreach (Item itItem in fiRess)
{
Object SearchKeyVal = null;
if (itItem.TryGetProperty(PidTagSearchKey, out SearchKeyVal))
{
Console.WriteLine(BitConverter.ToString((Byte[])SearchKeyVal));
}
Object InternetMessageIdVal = null;
if (itItem.TryGetProperty(PidTagInternetMessageId, out InternetMessageIdVal))
{
Console.WriteLine(InternetMessageIdVal);
}
}
ItemVeiwSet.Offset += fiRess.Items.Count;
} while (fiRess.MoreAvailable);
If you need larger properties like the Body using the LoadPropertiesForItems Method https://blogs.msdn.microsoft.com/exchangedev/2010/03/16/loading-properties-for-multiple-items-with-one-call-to-exchange-web-services/
I am trying to Get My live Emails to My wp App
Is it Possibly to get Emails into our App,
Now i am Getting Email Address and Date Of Birth and Some Other But Not getting Email's
My Xaml Code is Like this
<live:SignInButton ClientId="000000004C0FWD99" Scopes="wl.basic wl.offline_access wl.signin wl.contacts_birthday wl.emails" Branding="Windows" TextType="Login" d:LayoutOverrides="VerticalAlignment" SessionChanged="SignInButton_SessionChanged" />
C#
private void SignInButton_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
if (e != null && e.Session != null && e.Status == LiveConnectSessionStatus.Connected)
{
this.liveClient = new LiveConnectClient(e.Session);
Session = e.Session;
this.LoginIn();
}
else
this.GetUserProfile();
}
private void GetUserProfile()
{
LiveConnectClient clientGetMe = new LiveConnectClient(Session);
clientGetMe.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(clientGetMe_GetCompleted);
clientGetMe.GetAsync("me", null);
LiveConnectClient clientGetPicture = new LiveConnectClient(Session);
clientGetPicture.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(clientGetPicture_GetCompleted);
clientGetPicture.GetAsync("me/picture");
}
How can I get Email's with with Subject, From Address and Mail Content
is it Possibly
It's not possible. Only interaction with contacts and calendars is possible.
You can use the Live Connect APIs to do the following things in Hotmail:
Create new contacts, and read existing ones, in a user's contacts list.
Create, read, update, and delete a user's calendars and the calendars' associated events.
Subscribe a user to a public calendar, such as a list of holidays.
Use the friend finder feature to discover whether any of a users' contacts are also registered users of your website.
From: Live SDK developer guide
Exchange deletes Non-Delivery-Reports (NDRs) if the target is a public folder.
I want to write a Transport Agent (SMTPReceiveAgent, c#) to bypass this behavior. The goal is to change the NDR to a "normal" mail, that dont delete by exchange. I test some thinks around this and found no solution. Now i need help.
Here some questions:
It's easy to identify an NDR.
Content-Type: multipart/report;
report-type=delivery-status;
But what i have to change at the mail
to convert this to a "normal" mail? Change to multipart/alternative
not work or is not enough.
As an alternative i can create a new message with all infos captured
from the NDR. What is the best way to do this inside a
SMTReceiveAgents.OnSubmitted Event?
To create a copy from the public folder NDR for a normal user i tried args.Mailitem.Recipients.Add(new RoutingAddress("username#mydomain.com"))
in the EndOfDataHandler. This doesnt work. Why?
Any answers, hints or solutions?
Exchange 2007 NDRs to public Folders
Q1. You do have to change it to multipart/alternative, but also, you should find "Content-Type: message/delivery-status" and change it to text/plain but it isnt required
Q2. You could do this but your only options for the "original" message is to reject it to the sender, quarantine it or allow it..there is no delete / drop option.. but since it's going to a public folder it would be discarded.
If you go this route then enumerate the headers and body during EndOfHeadersEvent and then generate a new MailMessage object and include the headers and body from the original
Q3. That should work.. only reason that I can see it wouldnt work is if you're trying to send to an external recipient / domain that isn't an accepted domain on the server..if that is what youre trying to do then you'll need to create a mail contact with your real external address and then CC the NDR to the external contact
Below is the code I was able to accomplish what you're trying to do. The reason I hook into onRcpt and onEndOfHeaders is to check if the recipient entered is the public folder address..I found it faster than enumerating the rcpt list at the end of the header
void UserSendCounterSmtpReceiveAgent_OnRcptCommand(ReceiveCommandEventSource source, RcptCommandEventArgs e)
{
if(source == null || e == null)
{
return;
}
String recipient = e.RecipientAddress.ToString();
if (recipient.Equals("publicfolder#domain.com"))
{
this.testOnEndOfHeaders = true;
}
}
void UserSendCounterAgent_OnEndOfHeaders(ReceiveMessageEventSource source, EndOfHeadersEventArgs e)
{
if (source == null || e == null)
{
return;
}
if (testOnEndOfHeaders)
{
this.testOnEndOfHeaders = false;
Header obj = e.Headers.FindFirst("Content-Type");
if (obj.Value.Equals(#"multipart/report"))
{
obj.Value = #"multipart/alternative";
e.MailItem.Recipients.Add(new RoutingAddress("forwardto#domain.com"));
}
}
}