Unable to change Mail HTML body from VSTO Outook 2007 add-in - c#

The plot:
I am creating an outlook 2007 add-in using VSTO and C# in Visual Studio 2010. The purpose of the add-in is to cause tracking of mails sent to customers. The add-in should insert a tracking code into every outbound mail that later enables it to be recognized and auto archived once the customer replies. My goal is to insert a tracking code as an attribute in the opening tag of the mail's HTML:
<html tracking="ddfwer4w5c45cgx345gtg4g" ...>
This will be done in the event handler of the Application.ItemSend event:
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;
mail.HTMLBody = "<html tracking=\"4w5te45yv5e6ye57j57jr6\" ...";
}
}
The problem:
It seem to never change the HTMLBody property. And it does not throw any exceptions. It just does nothing. I rewrote this logic directly in VBA in Outlook, and tried to change the HTMLBody, but it still did not change. How I know it did not change the HTMLBody is by stepping through and hovering over the property to see the current value.
I am however able to make changes to the MailItem.Body property. But since I need to hide the tracking code in some way, the Body property does not help me at all. I also added the MailItem.Save() method after changing the HTMLBody property, but no change.
I thought that perhaps the ItemSend event is to late and I can't change the HTMLBody at that time anymore, but I can't find any event like BeforeSend or alike.
Any ideas would be much appreciated.

I use VB a lot more than C#, but I think I see your problem.
Check the function signature of Application_ItemSend. I see that Cancel is passed by reference; shouldn't Item be passed that way too?
void Application_ItemSend(ref object Item, ref bool Cancel)
{
// ...
}
If Item is passed by value, then your code is only manipulating the copy that was made; the original object isn't being touched. At least, that's how it worked in C++ (but my C++ experience is before .Net, so maybe that doesn't apply).

I had a similar need to put some ids into the message. Seems like Outlook thoroughly scrubs the email before sending it, so by the time it is sent, all the nice hidden properties that you set have been removed.
For my particular need, I ended up instead using the UserProperties element of MailItem. This persists from when the email is created to when it ends up in the Sent Items folder. Perhaps this approach can work for you if the customer reply is tried to the sent email through a conversation?
mailItem.UserProperties.Add("myid", Outlook.OlUserPropertyType.olText);
mailItem.UserProperties["myid"].Value = myid;

Related

Message Id (PR_INTERNET_MESSAGE_ID_W_TAG) Not Available in Event Handler After Sending An Email

I have an instance of Microsoft.Office.Interop.Outlook.Application and I add itemSendHandler to the Application.itemSend
Application.itemSend += itemSendHandler
I'm assuming inside the itemSendHandler, the email should already be sent and a Message Id should exist for the email. Yet the following code produces a null messageId:
private void itemSendEventHandler(object sentItem, ref bool Cancel)
}
string PR_INTERNET_MESSAGE_ID_W_TAG = "http://schemas.microsoft.com/mapi/proptag/0x1035001F";
PropertyAccessor propertyAccessor = ((MailItem)sentItem).PropertyAccessor;
// This is null? Why?
string messageId = (string)propertyAccessor.GetProperty(PR_INTERNET_MESSAGE_ID_W_TAG);
ThisAddIn.attemptToReleaseComObject(propertyAccessor);
{
But when I inspect the sent item immediately afterwards, through the code or through a tool (like OutlookSpy or MFCMAPI), the property exists. Why is messageId null during the send event handler?
You need to save the message first before accessing that property. Even then, PR_INTERNET_MESSAGE_ID might not be accessible in the cached mode - Outlook will not synchronize the item in the Sent Items folder with its online replica for the performance reasons, and only the online version of the message will have that property. You can open that message in the online mode using Extended MAPI (C++ or Delphi) or Redemption (any language) by using the MAPI_NO_CACHE flag, but there is no way to do that in OOM.

EWS Attached emails missing DatatimeReceived property after .Load()

I am currently working on a former colleague's project that deals with saving emails from Exchange to our ERP system.
But I have run into a strange problem. Sometimes when the system receives an email, that contains an email as an attachment. It throws an error because the DataTimeReceived date is not set. Even after a .Load();
private int HandleEmail(Item item, Folder moveToFolder, Folder moveToFailedFolder, Mailbox mailbox, int fatherId = 0, string uploaderEmail = "", bool isEmbeddedMail = false)
The way it work. is that the HandleEmail() method goes through the original email until it gets the if-check.
If the attachment is not a FileAttachment, and is an ItemAttachment. The following code is called.
else if (attachment is ItemAttachment)
{
var itemAttachment = attachment as ItemAttachment;
itemAttachment.Load(new PropertySet(ItemSchema.Attachments, ItemSchema.TextBody, EmailMessageSchema.Sender, EmailMessageSchema.DisplayCc, EmailMessageSchema.DateTimeReceived, EmailMessageSchema.From, ItemSchema.MimeContent, ItemSchema.Body, ItemSchema.TextBody, EmailMessageSchema.BccRecipients, ItemSchema.Attachments));
var item_ = itemAttachment.Item;
HandleEmail(item_, null, null, mailbox, (int)mailid, uploaderEmail, true); // The attached email is then looped through like it is an regular email instead of like an image.
}
In this check. It takes the item attachment and loads an additional property set, which contains some data that is needed.
Finally the method calls it self, but now with the Item Attachment as to read it like a regular email.
My issue is the fact, that some attached emails do not have the DatetimeReceived property set.
Instead this error is shown in its place.
Microsoft.Exchange.Webservice.Data.ServicesObjectPropertyException
And this expection message is thrown
You must load or assign this property before you can read its value
And I can't quite figure out why it only happens to some attached emails. When I look at the emails it self, it does have an received date. But for some reason I can't get it using .Load()
I've tried a few things, such as using .Load for both Item and ItemAttachment, both without getting anything worthwhile.
Tried looking into using the service.loadpropertiesforitems()
I've forgotten a few of the other things I've looked into, since I have been looking at this for a few days before the weekend aswell.
Two suggestions i would have is first try the EmailMessageSchema.DateTimeSent (which should be the same as DateTimeReceived) the difference maybe being caused when attaching a messages that was sent. The other thing would be enable tracing and have a look at the traces to see what is actually coming back from the server also maybe just try loading the BasePropertySet.FirstClassProperties rather then your custom set (just to test that property anyway)

Categories added to a MailItem before sending aren't persisted

I'm intercepting Outlook 2013's Application.ItemSend event in order to manipulate the categories assigned to a MailItem right before it's sent. Here's the event handler:
void Application_ItemSend(object Item, ref bool Cancel)
{
var mail = (Outlook.MailItem)Item;
mail.Categories = string.Join(";", "Foo", "Bar"); // Yes, the delimiter is ';' on my system.
mail.Save(); // Do I need this?
}
The problem is that the changes to the Categories property don't seem to be properly persisted. When I view the message in the Sent folder, it appears uncategorized.
Curiously, if I call mail.ShowCategoriesDialog() after changing the categories they appear checked as expected. This makes me suspect that I'm operating on a copy of the message.
What am I doing wrong?
It seems the issue was one of server configuration rather than my code. After connecting Outlook to GMail instead it worked as I expected.
It worked for me on Outlook 2013 ( 64-bit ) with a Gmail account configured. i.e. I can see the categories applied at all the steps - viz., while applying the categories in Visual Studio 2013, in the Outbox and also in the Sent Items folder.
Possible reason -
Some addin might be removing the categories in the Sent Item folder. try to disable other addin(s).

Obtain Mail Item details from .MSG attachment

Is it possible to obtain Outlook Mail Item details by dragging and dropping a single attachment from a .MSG file onto a C# application? My application currently separates the contents of a .MSG when this is dropped onto my application, however I want to go a step further and obtain sender, date/time received etc from a single attachment that is part of the .msg. This is what I'm trying at the moment:
Outlook.Application myApp = new Outlook.Application();
object selectedItem = myApp.ActiveExplorer().Selection[1];
Outlook.MailItem item = selectedItem as Outlook.MailItem;
string sender = item.SenderName;
When I try to cast selectedItem as an Outlook.Mail Item nothing happens. Any help with this would be appreciated
Thanks
Chris
Since my initial post I have been looking at other ways in which to obtain the information that Im looking for as I have not been successful with the method above..
I have looked at the following article http://msdn.microsoft.com/en-us/library/aa219397(v=office.11).aspx and implented the code in a test project. I know initially I asked if I could obtain the msg details from the attachement, however if a user drags an attachment from the current open message then I was wondering if it were possible to obtain the message details from the ActiveExplorer method.
At the point where:
myOlSel.Item(x).SenderName & ";"
Outlook prompts me with " A program is trying to access email address..." but at this the message box hangs and I cannot select one of the options. After doing some further reading I understand why this is in place but is there anyway around it?
Thanks
Chris
Maybe the selectedItem is null because there actually is no selected item at index 1?
I have the follwowing at is is working (althou it's with an Appointment item)
Inspector activeInspector = this.OutlookApp.ActiveInspector() as Inspector;
object currentItem = activeInspector.CurrentItem;
if (currentItem != null && currentItem is AppointmentItem)
{
AppointmentItem appItem = currentItem as AppointmentItem;
}
Perhaps you should use Selection[0]?

Drag'n'drop one or more mails from Outlook to C# WPF application

I'm working on a windows client written in WPF with C# on .Net 3.5 Sp1, where a requirement is that data from emails received by clients can be stored in the database. Right now the easiest way to handle this is to copy and paste the text, subject, contact information and time received manually using an arthritis-inducing amount of ctrl-c/ctrl-v.
I thought that a simple way to handle this would be to allow the user to drag one or more emails from Outlook (they are all using Outlook 2007 currently) into the window, allowing my app to extract the necessary information and send it to the backend system for storage.
However, a few hours googling for information on this seem to indicate a shocking lack of information about this seemingly basic task. I would think that something like this would be useful in a lot of different settings, but all I've been able to find so far have been half-baked non-solutions.
Does anyone have any advice on how to do this? Since I am just going to read the mails and not send anything out or do anything evil, it would be nice with a solution that didn't involve the hated security pop ups, but anything beats not being able to do it at all.
Basically, if I could get a list of all the mail items that were selected, dragged and dropped from Outlook, I will be able to handle the rest myself!
Thanks!
Rune
I found a great article that should do exactly what you need to.
UPDATE
I was able to get the code in that article working in WPF with a little tweaking, below are the changes you need to make.
Change all references from System.Windows.Forms.IDataObject to System.Windows.IDataObject
In the OutlookDataObject constructor, change
FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
To
FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("_innerData", BindingFlags.NonPublic | BindingFlags.Instance);
Change all DataFormats.GetFormat calls to DataFormats.GetDataFormat
Change the SetData implementation from
public void SetData(string format, bool autoConvert, object data)
{
this.underlyingDataObject.SetData(format, autoConvert, data);
}
TO
public void SetData(string format, object data, bool autoConvert)
{
this.underlyingDataObject.SetData(format, data, autoConvert);
}
With those changes, I was able to get it to save the messages to files as the article did. Sorry for the formatting, but numbered/bulleted lists don't work well with code snippets.
I found a lot of solutions suggesting you use the “FileGroupDescriptor” for all the file names and the “FileContents” on the DragEventArgs object to retrieve the data of each file. The “FileGroupDescriptor” works fine for the email message names, but “FileContents” returns a null because the implementation of the IDataObject in .Net cannot handle the IStorage object that is returned by COM.
David Ewen has a great explanation, excellent sample and code download that works great at http://www.codeproject.com/KB/office/outlook_drag_drop_in_cs.aspx.
In your Xaml you need to set up your Event:
<TextBlock
Name="myTextBlock"
Text="Drag something into here"
AllowDrop="True"
DragDrop.Drop="myTextBlock_Drop"
/>
Once you have Set AllowDrop = True and Set you drop event then go to the code behind and set up your event:
private void myTextBlock_Drop(object sender, DragEventArgs e)
{
// Mark the event as handled, so TextBox's native Drop handler is not called.
e.Handled = true;
Stream sr;
//Explorer
if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
//Do somthing
//Email Message Subject
if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
sr = e.Data.GetData("FileGroupDescriptor") as Stream;
StreamReader sr = new StreamReader(sr2);//new StreamReader(strPath, Encoding.Default);
//Message Subject
string strFullString = sr.ReadToEnd();
}
}
If you wish to break it down further you can use:
FILEDESCRIPTOR or FILECONTENTS as outline in the following article
your other option is to tie into outlooks MS Office Primary Interop Assemblies and break the message apart that way.
I think Shell Style Drag and Drop in .NET (WPF and WinForms) can help you. Once you can respond to drag drop using the COM Interfaces, you should be able to get the data out of outlook.
I assume that you have an Exchange server running behind Outlook.
What you can do is to retrieve the mail from the Exchange server and store its location in your database based on the mail's EntryID and StoreID. Here's a VB.Net snippet:
Imports Microsoft.Office.Interop
Public Class OutlookClientHandler
Private _application As Outlook.Application
Private _namespace As Outlook.NameSpace
Public Sub New()
If Process.GetProcessesByName("outlook".ToLower).Length > 0 Then
_application = New Outlook.Application
Else
Dim startInfo As ProcessStartInfo = New ProcessStartInfo("outlook.exe")
startInfo.WindowStyle = ProcessWindowStyle.Minimized
Process.Start(startInfo)
_application = New Outlook.Application
End If
End Sub
' Retrieves the specified e-mail from Outlook/Exchange via the MAPI
Public Function GetMailItem(ByVal entryID as String, ByVal storeID as String) As Outlook.MailItem
_namespace = _application.GetNamespace("MAPI")
Dim item As Outlook.MailItem
Try
item = _namespace.GetItemFromID(entryID, storeID)
Catch comex As COMException
item = Nothing ' Fugly, e-mail wasn't found!
End Try
Return item
End Function
End Class
I guess you are comfortable with using the MAPI, otherwise you can read up here:
http://msdn.microsoft.com/en-us/library/cc765775(v=office.12).aspx
To retrieve the selected e-mails from outlook:
Public Function GetSelectedItems() As List(Of Object)
Dim items As List(Of Object) = New List(Of Object)
For Each item As Object In _application.ActiveExplorer().Selection
items.Add(item)
Next
Return items
End Function
After you've retrieved the e-mails from Outlook you can just push them into your database! Save their EntryID and StoreID (you might want to store their parent's (the folder's) EntryID and StoreID as well).

Categories